Trabajo elaborado para la asignatura “Programación y manejo de datos en la era del Big Data” de la Universitat de València durante el curso 2021-2022. El repo del trabajo está aquí.

La página web de la asignatura y los trabajos de mis compañeros pueden verse aquí.


Librería de paquetes

Historia de la F1

LOGOS DE LA FORMULA 1

Logo de 1985 a 1986

Logo de 1985 a 1986

Logo de 1987 a 1993

Logo de 1987 a 1993

Logo de 1994 a 2017

Logo de 1994 a 2017

Logo Actual

LOGO desde 2018 hasta la actualidad

Protagonistas

#---PREPARACION DE LOS DATOS

pilotos <- rio::import(file = "./datos/drivers.csv")
resultados <- rio::import(file = "./datos/results.csv")
#str(resultados)
#str(pilotos)
#pilotos[, c(1)] <- sapply(pilotos[, c(1)], as.numeric)
#resultados[, c(3,6,9)] <- sapply(resultados[, c(3,6,9)], as.numeric)

#----------------------------------------------------------------------------------
#numero de carreras que ha corrido cada piloto

#explicacion de lo que hago, no se porque la variable sumatorio no la detecta como numerica aunque la pase a numerica, por tanto al ordenar con slice max no funciona, lo que he hecho es usar la funcion arrange, que ordena de menor a mayor, pero como queremos los que mas carreras han corrido, no me sirve de menor a mayor, por tanto he multiplicado la variable del sumatorio por -1, he usado arrange para que los que más carreras tienen salgan primero, y luego he vuelto a multiplicar por -1. luego he cogido mayores de 202 carreras, que son los 20 que más tienen, porque si hago slice se descuadra y te devuelve el df del principio

n_carreras <- resultados %>% group_by(driverId) %>% mutate(numero_carreras = sum(n())) %>% distinct(numero_carreras) %>% arrange(desc(numero_carreras)) #mutate(numero_carreras_final = numero_carreras*-1)

n_carreras_nom <- full_join(n_carreras, pilotos, c ("driverId" = "driverId")) %>% select(driverId, driverRef, numero_carreras)  %>%  filter(numero_carreras >= 202 ) #los 20 qque mas carreras tienen (no funciona usar slice_max)

#--------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# numero de victorias por piloto
victorias <- resultados %>% filter(position == "1") %>% group_by(driverId) %>% mutate(n_victorias = sum(n())) %>% distinct(n_victorias) %>% arrange(desc(n_victorias))#mutate(n_victorias_final = n_victorias*-1)

#aqui fusiono con el df de pilotos para que aparezca el nombre y no sólo el ID del piloto en cuestion, y hago lo mismo que en el apartado de arriba para ordenar
victorias_con_nombre <- full_join(victorias, pilotos, c ("driverId" = "driverId")) %>% select(driverId, nationality, driverRef, n_victorias)  #los 10 con mas victorias, tmp funciona slice_max
mas_victorias <- victorias_con_nombre %>%  filter(n_victorias >= 25 ) 




#-------------------------------------------------

#resultado medio
str(resultados)
#> 'data.frame':    25140 obs. of  18 variables:
#>  $ resultId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ raceId         : int  18 18 18 18 18 18 18 18 18 18 ...
#>  $ driverId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ constructorId  : int  1 2 3 4 1 3 5 6 2 7 ...
#>  $ number         : chr  "22" "3" "7" "5" ...
#>  $ grid           : int  1 5 7 11 3 13 17 15 2 18 ...
#>  $ position       : chr  "1" "2" "3" "4" ...
#>  $ positionText   : chr  "1" "2" "3" "4" ...
#>  $ positionOrder  : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ points         : num  10 8 6 5 4 3 2 1 0 0 ...
#>  $ laps           : int  58 58 58 58 58 57 55 53 47 43 ...
#>  $ time           : chr  "1:34:50.616" "+5.478" "+8.163" "+17.181" ...
#>  $ milliseconds   : chr  "5690616" "5696094" "5698779" "5707797" ...
#>  $ fastestLap     : chr  "39" "41" "41" "58" ...
#>  $ rank           : chr  "2" "3" "5" "7" ...
#>  $ fastestLapTime : chr  "1:27.452" "1:27.739" "1:28.090" "1:28.603" ...
#>  $ fastestLapSpeed: chr  "218.300" "217.586" "216.719" "215.464" ...
#>  $ statusId       : int  1 1 1 1 1 11 5 5 4 3 ...
resultados[, c(7)] <- sapply(resultados[, c(7)], as.numeric)
resultados[is.na(resultados)] <- 25 

resultado_medio <-  full_join(pilotos, resultados,  c ("driverId" = "driverId")) %>% select(driverId, driverRef, position) %>% group_by(driverId) %>% mutate(result_medio = mean(position)) %>% distinct (driverId, driverRef, result_medio) %>% arrange(result_medio)

#resultado medio en clasificacion
resultado_medio_clas <-  full_join(pilotos, resultados,  c ("driverId" = "driverId")) %>% select(driverId, driverRef, grid) %>% group_by(driverId) %>% mutate(result_medio_clas = mean(grid)) %>% distinct (driverId, driverRef, result_medio_clas)  %>% filter(result_medio_clas > 0) %>% arrange(result_medio_clas)  


#numero de vueltas liderando


#puntos por carrera (puntos/carrera)

puntos_x_carrera <-  full_join(pilotos, resultados,  c ("driverId" = "driverId")) %>% select(driverId, driverRef, points) %>% full_join(., n_carreras,  c ("driverId" = "driverId")) %>% group_by(driverId) %>% mutate(total_puntos = sum(points)) %>% distinct(driverId, driverRef, numero_carreras, total_puntos) %>% mutate(media_puntos = total_puntos/numero_carreras) %>% arrange(desc(media_puntos))

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])
#se necesita "mas_victorias"


gg_mas_victorias <- ggplot(mas_victorias, aes(x = reorder(driverRef, n_victorias), y = n_victorias )) + geom_bar(stat = "identity") + labs(x = "Piloto" , y = "Número de victorias")
gg_mas_victorias
#trabajo --> darle formato chulo, ya veremos este puente si le podemos meter dinámico o que

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom")
rm(list = ls()[!ls() %in% objetos_no_borrar])                       

Españoles por la F1

#--------------------------------------------------------------------------------------
#pilotos españoles

pilotos <- rio::import(file = "./datos/drivers.csv")

pilotos_esp <- pilotos %>% filter(nationality == "Spanish") %>% select(driverId, driverRef, nationality) 


#mas victorias de pilotos españoles
mas_victorias_esp <- full_join(victorias_con_nombre, pilotos, c ("driverId" = "driverId")) %>% filter(nationality.x == "Spanish") %>%  select(driverId, driverRef.x, n_victorias, nationality.x) 

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])
#-------------------------------------------------------------------------------------------
#datos de escuderias pa quien quiera hacer algo
#escuderias <- rio::import(file = "./datos/constructors.csv")
#escuderias2 <- rio::import(file = "./datos/constructor_standings.csv")
#result_escuderias <- rio::import(file = "./datos/constructor_results.csv")

#pilotos <- rio::import(file = "./datos/drivers.csv")
#resultados <- rio::import(file = "./datos/results.csv")
#carreras <- rio::import(file = "./datos/races.csv")
#escuderiasesp <- escuderias %>% filter(nationality == "Spanish") #escuderias españolas

#campeones_esc <- full_join(pilotos, resultados, c("driverId" = "driverId")) %>% full_join(., carreras, c("raceId" = "raceId")) %>% select(driverId, driverRef, nationality, constructorId, points, year, round) %>% full_join(., escuderias, c("constructorId" = "constructorId")) %>% select(driverId, driverRef, nationality.x, constructorId, points, year, name, round) %>%  group_by(year, driverRef) %>%  mutate(puntos_totales = cumsum(points)) %>% ungroup() %>% group_by(year) %>% slice_max(puntos_totales, n=1) %>% select(name, driverRef) %>% group_by(name, driverRef) %>% mutate(total_camp = sum( NN = n())) %>% arrange(name) 


#campeones_esc <- campeones_esc[!(campeones_esc$driverRef == 'max_verstappen'),] 

#library(treemap)
#library(d3treeR)


# basic treemap
#gg_esc_campeones <- treemap(campeones_esc,
            #index=c("name","driverRef"),
            #vSize="total_camp",
            #type="index",
            #vColor = "name",
            #palette = "Set2",
            #align.labels=list(
              #c("center", "center"), 
              #c("center", "bottom")),
            #title = "Escuderías con más campeones",
            #title.legend = "Escuderías")   
inter_camp <- d3tree2(gg_esc_campeones ,  rootname = "Escuderías y Campeones" )
inter_camp

Alonso (el nano)

#alonso vs compañeros de equipo

pilotos <- rio::import(file = "./datos/drivers.csv")
resultados <- rio::import(file = "./datos/results.csv")
escuderias <- rio::import(file = "./datos/constructors.csv")
carreras <- rio::import(file = "./datos/races.csv")

alovsall <- full_join(pilotos, resultados, c ("driverId" = "driverId")) %>%  select(driverRef, resultId, raceId, constructorId, position, points) %>% full_join(., escuderias, c ("constructorId" = "constructorId")) %>% select(driverRef, resultId, raceId, constructorId, position, position, points, name) %>% full_join(., carreras, c ("raceId" = "raceId")) %>% select(driverRef, resultId, raceId, constructorId, position, position, points, name.x, year, round)

#alo_vs_marques <- alovsall %>% filter(year == 2001, driverRef %in% c("alonso", "marques"), round <= 14)

alo_vs_trulli <- alovsall %>% filter(year %in% c(2003, 2004), driverRef %in% c("alonso", "trulli")) %>% slice(1:15, 17:67) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_fisichella <- alovsall %>% filter(year %in% c(2005, 2006), driverRef %in% c("alonso", "fisichella"))  %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_hamilton <- alovsall %>% filter(year %in% c(2007) ,driverRef %in% c("alonso", "hamilton"))  %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_piquet <- alovsall %>% filter(year %in% c(2008, 2009), driverRef %in% c("alonso", "piquet_jr")) %>% slice(1:28, 36:63) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

#alo_vs_grosjean <- alovsall %>% filter(year == 2009, driverRef %in% c("alonso", "grosjean"), round >= 11)

alo_vs_massa <- alovsall %>% filter(year %in% c(2010, 2011, 2013), driverRef %in% c("alonso", "massa")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_raikkonen <- alovsall %>% filter(year == 2014, driverRef %in% c("alonso", "raikkonen")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_button <- alovsall %>% filter(year %in% c(2015, 2016), driverRef %in% c("alonso", "button")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

alo_vs_vandoorne <- alovsall %>% filter(year %in% c(2017, 2018), driverRef %in% c("alonso", "vandoorne")) %>% slice(1:45, 47:81) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points))%>% ungroup() 

alo_vs_ocon <- alovsall %>% filter(year == 2021, driverRef %in% c("alonso", "ocon")) %>% group_by(driverRef, year) %>% mutate(puntos_acumulados = cumsum(points)) %>% ungroup()

ALO_VS_ALL <- full_join(alo_vs_trulli, alo_vs_fisichella, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round" , "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_hamilton, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_piquet, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_massa, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados"))  %>% 
  full_join(., alo_vs_raikkonen, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_button, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_vandoorne, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados")) %>% 
  full_join(., alo_vs_ocon, c("driverRef"= "driverRef", "resultId" = "resultId", "raceId" = "raceId", "constructorId" = "constructorId", "position" = "position", "points" = "points", "name.x" = "name.x", "year" = "year", "round" = "round", "puntos_acumulados" = "puntos_acumulados"))

objetos_no_borrar <- c("ALO_VS_ALL", "n_carreras_nom", "victorias_con_nombre")
rm(list = ls()[!ls() %in% objetos_no_borrar])


gc() #instruccion para que cargue el grafico, al ser tan complejo da error de no sé qué pero con esto funciona
#>           used (Mb) gc trigger  (Mb) max used  (Mb)
#> Ncells 1685519 90.1    3413672 182.4  3413672 182.4
#> Vcells 2969289 22.7    8388608  64.0  7648330  58.4
ggalo_vs_all <- ggplot(data = ALO_VS_ALL, aes(round, puntos_acumulados, color = driverRef)) +
  geom_line() +
  geom_point() + 
  labs(title = "Alonso contra el mundo",
       subtitle = "le das un carton con ruedas y aún te saca puntos",
       y = "Puntos", x = "") + facet_wrap( ~ year) + transition_reveal(round)

#ggalo_vs_all

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

Campeones del mundo

pilotos <- rio::import(file = "./datos/drivers.csv")
resultados <- rio::import(file = "./datos/results.csv")
escuderias <- rio::import(file = "./datos/constructors.csv")
carreras <- rio::import(file = "./datos/races.csv")



campeones <- full_join(pilotos, resultados, c("driverId" = "driverId")) %>% full_join(., carreras, c("raceId" = "raceId")) %>% select(driverId, driverRef, nationality, constructorId, points, year, round) %>% full_join(., escuderias, c("constructorId" = "constructorId")) %>% select(driverId, driverRef, nationality.x, constructorId, points, year, name, round) %>%  group_by(year, driverRef) %>%  mutate(puntos_totales = cumsum(points)) %>% ungroup() %>% group_by(year) %>% slice_max(puntos_totales, n=1) %>% ungroup() %>% group_by(driverRef)%>% mutate(total_campeonatos = sum(NN = n())) %>% distinct(driverRef, nationality.x, total_campeonatos) %>% arrange(nationality.x, total_campeonatos) #me quedo por aqui
  
campeones <- campeones[!(campeones$driverRef == 'max_verstappen'),]
id <- rownames(campeones)
campeones <- cbind(id=id, campeones)
campeones[, c(1)] <- sapply(campeones[, c(1)], as.numeric)




label_campeones <- campeones
number_of_bar <- nrow(label_campeones)

angle <- 90 - 360 * (label_campeones$id-0.5) /number_of_bar     # I substract 0.5 because the letter must have the angle of the center of the bars. Not extreme right(1) or extreme left (0)
label_campeones$hjust <- ifelse( angle < -90, 1, 0)
label_campeones$angle <- ifelse(angle < -90, angle+180, angle)



base_campeones <- campeones %>% 
  group_by(nationality.x) %>% 
  summarise(start=min(id), end=max(id)) %>% 
  rowwise() %>% 
  mutate(title=mean(c(start, end)))

grid_campeones <- base_campeones
grid_campeones$end <- grid_campeones$end[ c( nrow(grid_campeones), 1:nrow(grid_campeones)-1)] + 1
grid_campeones$start <- grid_campeones$start - 1
grid_campeones <- grid_campeones[-1,]

p <- ggplot(campeones, aes(x=as.factor(year), y=total_campeonatos, fill=nationality.x, color = nationality.x)) + geom_bar(aes(x=as.factor(id), y=total_campeonatos, fill=nationality.x), stat="identity", alpha=0.5) +# Add a val=100/75/50/25 lines. I do it at the beginning to make sur barplots are OVER it.
  geom_segment(data=grid_campeones, aes(x = 0, y = 8, xend = 31, yend = 8), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 0, y = 6, xend = 31, yend = 6), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 0, y = 4, xend = 31, yend = 4), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 0, y = 2, xend = 31, yend = 2), colour = "grey", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  
   # Add text showing the value of each 100/75/50/25 lines
  annotate("text", x = rep(max(campeones$id),4), y = c(2, 4, 6, 8), label = c("2", "4", "6", "8") , color="grey", size=3 , angle=0, fontface="bold", hjust=1) +
  
   geom_bar(aes(x=as.factor(id), y=total_campeonatos, fill=nationality.x), stat="identity", alpha=0.5) +
  ylim(-10,21) +
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text = element_blank(),
    axis.title = element_blank(),
    panel.grid = element_blank(),
    plot.margin = unit(rep(-1,4), "cm") ) +
  coord_polar() + 
  geom_text(data=label_campeones, aes(x=id, y=10, label=driverRef, hjust=hjust), color="black", fontface="bold",alpha=0.6, size=2.5, angle= label_campeones$angle, inherit.aes = FALSE ) +
  
   # Add base line information
  geom_segment(data=grid_campeones, aes(x = 0.70, y = -1, xend = 2.45, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE )  +
  geom_segment(data=grid_campeones, aes(x = 2.6, y = -1, xend = 3.55, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 3.65, y = -1, xend = 5.45, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 5.55, y = -1, xend = 7.35, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 7.5, y = -1, xend = 10.50, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 10.7, y = -1, xend = 19.20, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 19.4, y = -1, xend = 20.3, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 20.45, y = -1, xend = 23.4, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_segment(data=grid_campeones, aes(x = 23.65, y = -1, xend = 24.35, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_segment(data=grid_campeones, aes(x = 24.60, y = -1, xend = 27, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 27.2, y = -1, xend = 29.5, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 29.7, y = -1, xend = 30.5, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_segment(data=grid_campeones, aes(x = 30.7, y = -1, xend = 31.5, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) +
  geom_segment(data=grid_campeones, aes(x = 31.7, y = -1, xend = 32.5, yend = -1), colour = "black", alpha=1, size=0.3 , inherit.aes = FALSE ) + 
  geom_text(data=base_campeones, aes(x = title, y = -18, label=nationality.x), hjust=c(1,1,0,0), colour = "black", alpha=0.8, size=4, fontface="bold", inherit.aes = FALSE) 
 
#p 

Templos y tiempos

carreras <- rio::import(file = "./datos/races.csv")
circuitos <- rio::import(file = "./datos/circuits.csv")

carreras_21 <- full_join(carreras,circuitos, c("circuitId" = "circuitId")) %>%
  filter(year=="2020") %>%
  select(round, name.x, name.y, date, location,country, lat, lng, alt) %>%
  arrange(round) %>% 
  mutate(round2 = round) 

carreras_21_v2 <- carreras_21[, c(1, 4, 10, 2, 3, 5, 6, 7, 8, 9)]
carreras_21_v2 <- carreras_21_v2%>%  unite(. ,variables, c(1, 5, 7), sep = "; ")

#pruebas para mapas, no ejecutar de momento
#library(widgetframe)
#library(leaflet)
#l <- leaflet() %>% setView(lat = 45.61560, lng = 9.281110, zoom=1)
#frameWidget(l) 

#mapaCiudadyPueblomayorinciAcu <- leaflet() %>%
 # setView(lng = -0.243591, lat = 38.821, zoom = 7) %>% 
  #addMarkers(lng = -0.243591, lat = 38.821 , popup = "Vall de Gallinera")%>%
  #setView(lng = -0.418598, lat = 40.2011, zoom = 7) %>% 
  #addMarkers(lng = -0.418598, lat = 40.2011 , popup = "Villahermosa del rio") %>% addTiles()
#mapaCiudadyPueblomayorinciAcu

#MAPA DEL MUNDO DE LA OSTIA NO TOCAR, pongo como comentario para que no tarde tanto al knitear


globo_circ <-create_globe() %>% globe_pov(45.61560, 9.281110) %>% globe_bars(coords(lat, lng, label  = variables, color = round2), data = carreras_21_v2)  %>% scale_bars_color()
#globo_circ

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])
circuitos <- rio::import(file = "./datos/circuits.csv")
tiempos <- rio::import(file = "./datos/lap_times.csv")
carreras <- rio::import(file = "./datos/races.csv")
pilotos <- rio::import(file = "./datos/drivers.csv")

circuitos_gp <- full_join(carreras, circuitos, c("circuitId" = "circuitId")) #asocio las carreras a su circuito
tiemposvuelta_x_carrera <- full_join(circuitos_gp, tiempos, c ("raceId" = "raceId")) #tiempo de las vueltas por cada carrera

tiemposvuelta_x_carrera <- full_join(tiemposvuelta_x_carrera, pilotos, c ("driverId" = "driverId")) %>%  select(circuitId, name.y, driverId, driverRef, time.y, lap,position, year, country) #fusiono con el df de pilotos para asociar cada vuelta al nombre del piloto que la hizo


#calculo el record de cada circuito, filtrando el minimo de los tiempos en cada circuito
record_de_circuito <- tiemposvuelta_x_carrera %>% group_by(name.y) %>% slice_min(time.y, n=1)

#numero de records de circuito que tiene cada piloto
record_x_piloto <- record_de_circuito %>% group_by(driverId) %>% mutate(numero_records = sum(n())) %>% select(driverId, driverRef, numero_records) %>% distinct(driverRef, numero_records) %>% arrange(desc(numero_records))


# meter mapa del mundo con la ubicacion de los circuitos en la temporada 2021
#------------------------------------------------------------

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

Capítulo oscuro del deporte

#creo df de muertes de formula 1
muertesf1 <- data.frame(
  "orden" = 1:42,
  "driverRef" = c("Chet Miller", "Carl Scaraborough", "Onofre Marimon", "Manny Ayulo", "Bill Vukovich", "Alberto Ascari","Eugenio Castellotti", "Keith Andrews", "Pat O'Connor", "Luigi Musso", "Peter Collins", "Stuart Lewis-Evans", "Jerry Unser", "Bob Cortner", "Ivor Bueb", "Chris Bristow", "Alan Stacey", "Giulio Cabianca", "Wolfgang von Trips", "Carel Godin de Beaufort", "John Taylor", "Lorenzo Bandini", "Bob Anderson", "Jo Schlesser", "Gerhard Mitter", "Piers Courage", "Jochen Rindt", "Jo Siffert", "Roger Williamson", "François Cevert", "Peter Revson", "Helmuth Koinigg", "Mark Donohue", "Tom Pryce", "Ronnie Peterson", "Patrick Depailler", "Gilles Villeneuve", "Riccardo Paletti", "Elio de Angelis", "Roland Ratzenberger", "Ayrton Senna", "Jules Bianchi"),
  "nationality" = c("American", "American", "Argentine", "American", "American", "Italian","Italian", "American", "American", "Italian", "British", "British", "American", "American", "British", "British", "British", "Italian", "German", "Dutch", "British", "Italian" , "British", "French", "German", "British", "Austrian", "Swiss", "British", "French", "American", "Austrian", "American", "British", "Swedish", "French", "Canadian", "Italian", "Italian", "Austrian", "Brazilian", "French"),
  "dod" = c(1953, 1953, 1954, 1955, 1955, 1955, 1957, 1957, 1958,1958,1958,1958,1959,1959,1959,1960, 1960,1961,1961,1964,1966, 1967, 1967,1968,1969, 1970,1970,1971,1973,1973,1974,1974,1975,1977,1978,1980,1982,1982,1986,1994,1994,2014))
#no pongo las comillas en los años para que se creen directamente como observaciones numericas

#creo un df con todos los años para luego fusionarlo, ya que no hay muertes todos los años 
anyos <- data.frame(
  "orden" = 1:71,
  "año" = c(1950:2020))

#sumatorio de las muertes por año
muertes_anyo <- muertesf1 %>% group_by(dod) %>% mutate(muertesxanyo = sum(n())) %>% distinct(dod, muertesxanyo) 

#fusiono los 2 dfs para que tenga en cuenta los años donde no hay muertes
muertesf1_final <- full_join(muertes_anyo, anyos, c("dod" = "año")) %>% select(dod,muertesxanyo) %>% arrange(dod)

#convierto los N/A en 0, es decir, cuando no hay observaciones, ha habido 0 muertes
muertesf1_final[is.na(muertesf1_final)] <- 0

#grafico de las muertes por cada año + la tendencia negativa ea lo largo de la historia
gg_muertes <- ggplot(muertesf1_final, aes(x = dod, y = muertesxanyo )) + geom_point() + geom_line() + geom_smooth() + labs(x = "Año" , y = "Número de muertes")
gg_muertes


objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

1. Introducción

Tenemos pensado elaborar el trabajo en equipo sobre Formula 1, una competición de la que somos muy aficionados, entre otras cosas por la importancia que tienen los datos a la hora de formalizar las estrategias en la competición.

Remando a contracorriente

#mas posiciones remontadas en una carrera gran premio

tiempos <- rio::import(file = "./datos/lap_times.csv")
carreras <- rio::import(file = "./datos/races.csv")
resultados <- rio::import(file = "./datos/results.csv")
circuitos <- rio::import(file = "./datos/circuits.csv")
pilotos <- rio::import(file = "./datos/drivers.csv")

resultados[, c(6,9)] <- sapply(resultados[, c(6,9)], as.numeric) #transformo variables grid y positionOrder en numerico
str(resultados) # para comprobarlo
#> 'data.frame':    25140 obs. of  18 variables:
#>  $ resultId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ raceId         : int  18 18 18 18 18 18 18 18 18 18 ...
#>  $ driverId       : int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ constructorId  : int  1 2 3 4 1 3 5 6 2 7 ...
#>  $ number         : chr  "22" "3" "7" "5" ...
#>  $ grid           : num  1 5 7 11 3 13 17 15 2 18 ...
#>  $ position       : chr  "1" "2" "3" "4" ...
#>  $ positionText   : chr  "1" "2" "3" "4" ...
#>  $ positionOrder  : num  1 2 3 4 5 6 7 8 9 10 ...
#>  $ points         : num  10 8 6 5 4 3 2 1 0 0 ...
#>  $ laps           : int  58 58 58 58 58 57 55 53 47 43 ...
#>  $ time           : chr  "1:34:50.616" "+5.478" "+8.163" "+17.181" ...
#>  $ milliseconds   : chr  "5690616" "5696094" "5698779" "5707797" ...
#>  $ fastestLap     : chr  "39" "41" "41" "58" ...
#>  $ rank           : chr  "2" "3" "5" "7" ...
#>  $ fastestLapTime : chr  "1:27.452" "1:27.739" "1:28.090" "1:28.603" ...
#>  $ fastestLapSpeed: chr  "218.300" "217.586" "216.719" "215.464" ...
#>  $ statusId       : int  1 1 1 1 1 11 5 5 4 3 ...
#mayores remontadas de la historia, se resta posicion de salida - posicion final
puestos_remontados <- resultados %>% mutate(remontados = grid - positionOrder) %>% select(raceId, driverId, grid, positionOrder, remontados) 


#de toda la historia
circuitos_gp <- full_join(carreras, circuitos, c("circuitId" = "circuitId")) %>% select(circuitId, name.y, raceId, year)

ptos_remont_carrera <- inner_join(puestos_remontados, circuitos_gp)

puestos_remont_piloto <- full_join(pilotos, ptos_remont_carrera, c("driverId" = "driverId")) %>% slice_max(remontados, n=10) %>% select(driverId, driverRef,name.y,year, raceId, grid, positionOrder, remontados)

#------------------------------

# de la hisotoria reciente
circuitos_gp_recient <- full_join(carreras, circuitos, c("circuitId" = "circuitId")) %>% select(circuitId, name.y, raceId, year) %>% filter(year >= 1995)

ptos_remont_carrera_recient <- inner_join(puestos_remontados, circuitos_gp_recient)

puestos_remont_piloto_recient <- full_join(pilotos, ptos_remont_carrera_recient, c("driverId" = "driverId")) %>% slice_max(remontados, n=10) %>% select(driverId, driverRef, name.y, year,raceId, grid, positionOrder, remontados) %>% slice(1:4,6:8,10) %>% arrange(desc(remontados))

ggremontados <- ggplot(puestos_remont_piloto_recient, aes(x = reorder(driverRef, remontados), remontados)) + geom_bar(stat = "identity") + coord_flip() + labs(x = "Pilotos", y = "Nº de puestos remontados" )
ggremontados

objetos_no_borrar <- c("victorias_con_nombre", "n_carreras_nom", "mas_victorias")
rm(list = ls()[!ls() %in% objetos_no_borrar])

ALONSO vs HAMILTON

#se necesita tener cargado "n_carreras_nom", "victorias_con_nombre"

fotos_ALO_vs_HAM <- c("./imagenes/pilotos/alonso.png", "./imagenes/pilotos/hamilton.png")
fotos_esp_ing <- c("./imagenes/paises/espanya.png", "./imagenes/paises/uk.png")
n_carreras_alo_ham <- n_carreras_nom %>% filter(driverRef %in% c("alonso", "hamilton"))

n_victorias_alo_ham <- victorias_con_nombre %>% filter(driverRef %in% c("alonso", "hamilton"))

alo_vs_ham <- full_join(n_carreras_alo_ham, n_victorias_alo_ham, c("driverRef"= "driverRef")) %>% select( driverRef, numero_carreras, n_victorias) %>% add_column(fotos_esp_ing, fotos_ALO_vs_HAM) 

library(gt)
alo_vs_ham_tabla <- alo_vs_ham %>% gt() %>% text_transform( locations = cells_body(columns = c(fotos_esp_ing)), fn = function(x) {gt::local_image(x, height = 50)}) %>% text_transform( locations = cells_body(columns = c(fotos_ALO_vs_HAM)), fn = function(x) {gt::local_image(x, height = 100)}) %>% tab_header(title = md("**Alonso vs Hamilton**"), subtitle = md("Comparación")) %>%   cols_label(
    driverRef = html(""),
    numero_carreras = html("Nº carreras"),
    n_victorias = html("Nº victorias"),
    fotos_esp_ing = html("País"),
    fotos_ALO_vs_HAM = html("")) %>%  
  tab_options(table.background.color = "gray13",   table.font.color.light = "cyan") %>% 
  cols_align(align = "center",
  columns = everything())

alo_vs_ham_tabla
Alonso vs Hamilton
Comparación
Nº carreras Nº victorias País
alonso 323 32
hamilton 275 98
#audiencias


audiencias <- rio::import(file = "./datos/audienciasF1.csv")

ggaudiencias <- ggplot(audiencias, aes(x = Año, y = numero_espectadores)) + 
  geom_histogram (stat = "identity") + 
  labs(x = "Año", y = "Numero de espectadores" ) +
    scale_x_continuous(
    breaks = seq(2004, 2020, 1),
    limits = c(2003, 2021)) #+ transition_reveal(Año)
ggaudiencias

rm(list = ls())

#presupuestos

presupuestos <- read_excel("datos/presupuestos.xlsx")

gg_presup <- ggplot(presupuestos, aes(year, Presupuesto, color = Escuderia)) + 
  geom_point() + geom_line() + 
  labs(x = "Año", y = "Presupuesto en €" ) +
    scale_x_continuous(
    breaks = seq(2015, 2023, 1),
    limits = c(2014, 2024)) + 
  scale_y_continuous( breaks = seq(0, 700000000, 100000000),
    limits = c(0, 600000000)) 

ggplotly(gg_presup)
rm(list = ls())

THE PLAN

2. Datos

Hemos encontrado en kaggle bastantes conjuntos de datos con los que poder trabajar, pero especialmente este, que posee gran variedad de datos en lo referente a pilotos, resultados, circuitos, tiempos, etc… Consideramos que para empezar a trabajar será suficiente, y en función de como vayamos dirigiendo el trabajo, buscaremos diferentes conjunto de datos con los que apoyarnos.

3. Trabajos en los que nos vamos a basar

Con los datos que hemos encontrado, existen una serie de códigos que ya trabajan con estos datos, especialmente este, que ha conseguido realizar análisis con este conjunto de datos y varias ilustraciones muy llamativas, por lo que podremos tomarlo como referencia durante el inicio del trabajo

“Nadie es más rápido que el nano”

LS0tDQp0aXRsZTogIkFsdCArIEZvcm11bGEgMSINCmF1dGhvcjogIkNheWV0YW5vIFJvbWVybyBNb250ZWFndWRvIChjYXJvbW9uM0BhbHVtbmkudXYuZXMpICBcblxuIEFsZWphbmRybyBHYXJjw61hIFNlZ2FycmEgKGFnYXJzZTRAYWx1bW5pLnV2LmVzKSAgXG4gXG4gQ2FybG9zIEdhcmPDrWEgQ2FzdGlsbGEgKGdhcmNhczhAYWx1bW5pLnV2LmVzKS4gXG5cbiBVbml2ZXJzaXRhdCBkZSBWYWzDqG5jaWEiDQpkYXRlOiAiRGljaWVtYnJlIGRlIDIwMjEgKGFjdHVhbGl6YWRvIGVsIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVknKWApIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgICNjc3M6ICIuL2Fzc2V0cy9teV9jc3NfZmlsZS5jc3MiDQogICAgdGhlbWU6IGRhcmtseQ0KICAgIGhpZ2hsaWdodDogdGFuZ28gDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzIA0KICAgIHRvY19mbG9hdDogDQogICAgICBjb2xsYXBzZWQ6IHRydWUNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UNCiAgICBkZl9wcmludDoga2FibGUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCjwhLS0tIGNodW5rIHBhcmEgcXVlIGxhIGZ1ZW50ZSBzZWEgbGEgb2ZpY2lhbCBkZSBsYSBGb3JtdWxhIDEgLS0+DQpgYGB7Y3NzLCBlY2hvPUZBTFNFfSANCkBmb250LWZhY2Ugew0KICBmb250LWZhbWlseTogRjE7DQogIHNyYzogdXJsKGh0dHBzOi8vd3d3LmZvcm11bGExLmNvbS9ldGMvZGVzaWducy9mb20td2Vic2l0ZS9mb250cy9GMVJlZ3VsYXIvRm9ybXVsYTEtUmVndWxhci50dGYpOw0KfQ0KDQpzcGFuew0KICBmb250LWZhbWlseTogRjE7DQp9DQoNCmF7DQogIGZvbnQtZmFtaWx5OiBGMTsNCn0NCg0KLm5hdi1waWxscz5saS5hY3RpdmU+YTpmb2N1cyB7DQogICAgY29sb3I6ICNmZmZmZmY7DQogICAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmF5Ow0KfQ0KDQouY29udGFpbmVyLWZsdWlkLCAuY29udGFpbmVyLWZsdWlkIGgxIHsNCiAgICBmb250LWZhbWlseTogRjE7DQogICAgbGluZS1oZWlnaHQ6IDEuNzsNCn0NCg0KLmNvbnRhaW5lci1mbHVpZCBwIHsNCiAgICBmb250LWZhbWlseTogRjE7DQogICAgY29sb3I6Y3lhbjsNCn0NCg0KaDEsaDIsaDMsaDQsaDUsaDYscCwgdGFibGUgew0KICBmb250LWZhbWlseTogRjE7DQogIGNvbG9yOiBjeWFuDQp9DQpgYGANCg0KDQoNCmBgYHtyIGNodW5rLXNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWwgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgI3Jlc3VsdHMgPSAiaG9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSwgY2FjaGUucGF0aCA9ICIvY2FjaGVzLyIsIGNvbW1lbnQgPSAiIz4iLA0KICAgICAgICAgICAgICAgICAgICAgICNmaWcud2lkdGggPSA3LCAjZmlnLmhlaWdodD0gNywgICANCiAgICAgICAgICAgICAgICAgICAgICAjb3V0LndpZHRoID0gNywgb3V0LmhlaWdodCA9IDcsDQogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCAgZmlnLnNob3cgPSAiaG9sZCIsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFzcCA9IDAuNjI4LCBvdXQud2lkdGggPSAiNzUlIiwgZmlnLmFsaWduID0gImNlbnRlciIpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZGV2ID0gInBuZyIsIGRldi5hcmdzID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKQ0KYGBgDQoNCmBgYHtyIG9wdGlvbnMtc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KSAjLSBwYXJhIHF1aXRhciBsYSBub3RhY2nDs24gY2llbnTDrWZpY2ENCm9wdGlvbnMoInlhbWwuZXZhbC5leHByIiA9IFRSVUUpIA0KYGBgDQoNCg0KYGBge3Iga2xpcHB5LCBlY2hvID0gRkFMU0V9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoInRvcCIsICJyaWdodCIpKSAjLSByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpDQpgYGANCg0KDQoNCjxociBjbGFzcz0ibGluZWEtYmxhY2siPg0KDQo8IS0tIEVsIHDDoXJyYWZvIGRlIGFiYWpvIGhhcyBkZSBkZWphcmxvIGNhc2kgaWd1YWwsIHNvbG8gSEFTIGRlIFNVU1RJVFVJUiAicGVyZXpwNDQiIHBvciB0dSB1c3VhcmlvIGRlIEdpdGh1Yi0tPg0KVHJhYmFqbyBlbGFib3JhZG8gcGFyYSBsYSBhc2lnbmF0dXJhICJQcm9ncmFtYWNpw7NuIHkgbWFuZWpvIGRlIGRhdG9zIGVuIGxhIGVyYSBkZWwgQmlnIERhdGEiIGRlIGxhIFVuaXZlcnNpdGF0IGRlIFZhbMOobmNpYSBkdXJhbnRlIGVsIGN1cnNvIDIwMjEtMjAyMi4gRWwgcmVwbyBkZWwgdHJhYmFqbyBlc3TDoSBbYXF1w61dKGh0dHBzOi8vZ2l0aHViLmNvbS9jYXlldGFubzEwOC90cmFiYWpvX0JpZ0RhdGFfZXF1aXBvKXt0YXJnZXQ9Il9ibGFuayJ9LiANCg0KPCEtLSBFbCBww6FycmFmbyBkZSBhYmFqbyBoYXMgZGUgZGVqYXJsbyBleGFjdGFtZW50ZSBpZ3VhbCwgTk8gaGFzIGRlIGNhbWJpYXIgbmFkYS0tPg0KDQpMYSBww6FnaW5hIHdlYiBkZSBsYSBhc2lnbmF0dXJhIHkgbG9zIHRyYWJham9zIGRlIG1pcyBjb21wYcOxZXJvcyBwdWVkZW4gdmVyc2UgW2FxdcOtXShodHRwczovL3BlcmV6cDQ0LmdpdGh1Yi5pby9pbnRyby1kcy0yMS0yMi13ZWIvMDctdHJhYmFqb3MuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4NCg0KPGhyIGNsYXNzPSJsaW5lYS1yZWQiPg0KDQojIExpYnJlcsOtYSBkZSBwYXF1ZXRlcw0KDQpgYGB7ciBwYWNrYWdlcy1zZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGtsaXBweSkgICMtIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJybGVzdXIva2xpcHB5IikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHJpbykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2dhbmltYXRlKQ0KbGlicmFyeShnZ1RoZW1lQXNzaXN0KQ0KbGlicmFyeShnbG9iZTRyKSAgI3JlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJKb2huQ29lbmUvZ2xvYmU0ciIpDQpsaWJyYXJ5KHJlbW90ZXMpIA0KbGlicmFyeShndCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHRyZWVtYXApICNpbnN0YWxsLnBhY2thZ2VzKHRyZWVtYXApDQpsaWJyYXJ5KGQzdHJlZVIpICNyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZDN0cmVlUi9kM3RyZWVSIikNCmBgYA0KDQojIEhpc3RvcmlhIGRlIGxhIEYxDQoNCg0KIyBMT0dPUyBERSBMQSBGT1JNVUxBIDEgIHsudGFic2V0fQ0KDQojIyBMb2dvIGRlIDE5ODUgYSAxOTg2DQoNCg0KPGNlbnRlcj4NCiFbTG9nbyBkZSAxOTg1IGEgMTk4Nl0oLi9pbWFnZW5lcy9sb2dvcy8xOTg1LTE5ODYucG5nKXt3aWR0aD00MDAgaGVpZ2h0PTcwfQ0KPC9jZW50ZXI+DQoNCg0KDQoNCg0KDQojIyBMb2dvIGRlIDE5ODcgYSAxOTkzDQo8Y2VudGVyPg0KIVtMb2dvIGRlIDE5ODcgYSAxOTkzXSguL2ltYWdlbmVzL2xvZ29zLzE5ODctMTk5My5qcGcpe3dpZHRoPTI1MCBoZWlnaHQ9MzAwfQ0KPC9jZW50ZXI+DQoNCiMjIExvZ28gZGUgMTk5NCBhIDIwMTcNCjxjZW50ZXI+DQohW0xvZ28gZGUgMTk5NCBhIDIwMTddKC4vaW1hZ2VuZXMvbG9nb3MvMTk5NC0yMDE3LnBuZyl7d2lkdGg9NDUwIGhlaWdodD0zMDB9DQo8L2NlbnRlcj4NCiMjIExvZ28gQWN0dWFsDQo8Y2VudGVyPg0KIVtMT0dPIGRlc2RlIDIwMTggaGFzdGEgbGEgYWN0dWFsaWRhZF0oLi9pbWFnZW5lcy9sb2dvcy8yMDE4LS5wbmcpe3dpZHRoPTQwMCBoZWlnaHQ9MzAwfQ0KPC9jZW50ZXI+DQoNCiMgUHJvdGFnb25pc3Rhcw0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCg0KIy0tLVBSRVBBUkFDSU9OIERFIExPUyBEQVRPUw0KDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQpyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQojc3RyKHJlc3VsdGFkb3MpDQojc3RyKHBpbG90b3MpDQojcGlsb3Rvc1ssIGMoMSldIDwtIHNhcHBseShwaWxvdG9zWywgYygxKV0sIGFzLm51bWVyaWMpDQojcmVzdWx0YWRvc1ssIGMoMyw2LDkpXSA8LSBzYXBwbHkocmVzdWx0YWRvc1ssIGMoMyw2LDkpXSwgYXMubnVtZXJpYykNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiNudW1lcm8gZGUgY2FycmVyYXMgcXVlIGhhIGNvcnJpZG8gY2FkYSBwaWxvdG8NCg0KI2V4cGxpY2FjaW9uIGRlIGxvIHF1ZSBoYWdvLCBubyBzZSBwb3JxdWUgbGEgdmFyaWFibGUgc3VtYXRvcmlvIG5vIGxhIGRldGVjdGEgY29tbyBudW1lcmljYSBhdW5xdWUgbGEgcGFzZSBhIG51bWVyaWNhLCBwb3IgdGFudG8gYWwgb3JkZW5hciBjb24gc2xpY2UgbWF4IG5vIGZ1bmNpb25hLCBsbyBxdWUgaGUgaGVjaG8gZXMgdXNhciBsYSBmdW5jaW9uIGFycmFuZ2UsIHF1ZSBvcmRlbmEgZGUgbWVub3IgYSBtYXlvciwgcGVybyBjb21vIHF1ZXJlbW9zIGxvcyBxdWUgbWFzIGNhcnJlcmFzIGhhbiBjb3JyaWRvLCBubyBtZSBzaXJ2ZSBkZSBtZW5vciBhIG1heW9yLCBwb3IgdGFudG8gaGUgbXVsdGlwbGljYWRvIGxhIHZhcmlhYmxlIGRlbCBzdW1hdG9yaW8gcG9yIC0xLCBoZSB1c2FkbyBhcnJhbmdlIHBhcmEgcXVlIGxvcyBxdWUgbcOhcyBjYXJyZXJhcyB0aWVuZW4gc2FsZ2FuIHByaW1lcm8sIHkgbHVlZ28gaGUgdnVlbHRvIGEgbXVsdGlwbGljYXIgcG9yIC0xLiBsdWVnbyBoZSBjb2dpZG8gbWF5b3JlcyBkZSAyMDIgY2FycmVyYXMsIHF1ZSBzb24gbG9zIDIwIHF1ZSBtw6FzIHRpZW5lbiwgcG9ycXVlIHNpIGhhZ28gc2xpY2Ugc2UgZGVzY3VhZHJhIHkgdGUgZGV2dWVsdmUgZWwgZGYgZGVsIHByaW5jaXBpbw0KDQpuX2NhcnJlcmFzIDwtIHJlc3VsdGFkb3MgJT4lIGdyb3VwX2J5KGRyaXZlcklkKSAlPiUgbXV0YXRlKG51bWVyb19jYXJyZXJhcyA9IHN1bShuKCkpKSAlPiUgZGlzdGluY3QobnVtZXJvX2NhcnJlcmFzKSAlPiUgYXJyYW5nZShkZXNjKG51bWVyb19jYXJyZXJhcykpICNtdXRhdGUobnVtZXJvX2NhcnJlcmFzX2ZpbmFsID0gbnVtZXJvX2NhcnJlcmFzKi0xKQ0KDQpuX2NhcnJlcmFzX25vbSA8LSBmdWxsX2pvaW4obl9jYXJyZXJhcywgcGlsb3RvcywgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG51bWVyb19jYXJyZXJhcykgICU+JSAgZmlsdGVyKG51bWVyb19jYXJyZXJhcyA+PSAyMDIgKSAjbG9zIDIwIHFxdWUgbWFzIGNhcnJlcmFzIHRpZW5lbiAobm8gZnVuY2lvbmEgdXNhciBzbGljZV9tYXgpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIG51bWVybyBkZSB2aWN0b3JpYXMgcG9yIHBpbG90bw0KdmljdG9yaWFzIDwtIHJlc3VsdGFkb3MgJT4lIGZpbHRlcihwb3NpdGlvbiA9PSAiMSIpICU+JSBncm91cF9ieShkcml2ZXJJZCkgJT4lIG11dGF0ZShuX3ZpY3RvcmlhcyA9IHN1bShuKCkpKSAlPiUgZGlzdGluY3Qobl92aWN0b3JpYXMpICU+JSBhcnJhbmdlKGRlc2Mobl92aWN0b3JpYXMpKSNtdXRhdGUobl92aWN0b3JpYXNfZmluYWwgPSBuX3ZpY3RvcmlhcyotMSkNCg0KI2FxdWkgZnVzaW9ubyBjb24gZWwgZGYgZGUgcGlsb3RvcyBwYXJhIHF1ZSBhcGFyZXpjYSBlbCBub21icmUgeSBubyBzw7NsbyBlbCBJRCBkZWwgcGlsb3RvIGVuIGN1ZXN0aW9uLCB5IGhhZ28gbG8gbWlzbW8gcXVlIGVuIGVsIGFwYXJ0YWRvIGRlIGFycmliYSBwYXJhIG9yZGVuYXINCnZpY3Rvcmlhc19jb25fbm9tYnJlIDwtIGZ1bGxfam9pbih2aWN0b3JpYXMsIHBpbG90b3MsIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgbmF0aW9uYWxpdHksIGRyaXZlclJlZiwgbl92aWN0b3JpYXMpICAjbG9zIDEwIGNvbiBtYXMgdmljdG9yaWFzLCB0bXAgZnVuY2lvbmEgc2xpY2VfbWF4DQptYXNfdmljdG9yaWFzIDwtIHZpY3Rvcmlhc19jb25fbm9tYnJlICU+JSAgZmlsdGVyKG5fdmljdG9yaWFzID49IDI1ICkgDQoNCg0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiNyZXN1bHRhZG8gbWVkaW8NCnN0cihyZXN1bHRhZG9zKQ0KDQpyZXN1bHRhZG9zWywgYyg3KV0gPC0gc2FwcGx5KHJlc3VsdGFkb3NbLCBjKDcpXSwgYXMubnVtZXJpYykNCnJlc3VsdGFkb3NbaXMubmEocmVzdWx0YWRvcyldIDwtIDI1IA0KDQpyZXN1bHRhZG9fbWVkaW8gPC0gIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCAgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIHBvc2l0aW9uKSAlPiUgZ3JvdXBfYnkoZHJpdmVySWQpICU+JSBtdXRhdGUocmVzdWx0X21lZGlvID0gbWVhbihwb3NpdGlvbikpICU+JSBkaXN0aW5jdCAoZHJpdmVySWQsIGRyaXZlclJlZiwgcmVzdWx0X21lZGlvKSAlPiUgYXJyYW5nZShyZXN1bHRfbWVkaW8pDQoNCiNyZXN1bHRhZG8gbWVkaW8gZW4gY2xhc2lmaWNhY2lvbg0KcmVzdWx0YWRvX21lZGlvX2NsYXMgPC0gIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCAgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIGdyaWQpICU+JSBncm91cF9ieShkcml2ZXJJZCkgJT4lIG11dGF0ZShyZXN1bHRfbWVkaW9fY2xhcyA9IG1lYW4oZ3JpZCkpICU+JSBkaXN0aW5jdCAoZHJpdmVySWQsIGRyaXZlclJlZiwgcmVzdWx0X21lZGlvX2NsYXMpICAlPiUgZmlsdGVyKHJlc3VsdF9tZWRpb19jbGFzID4gMCkgJT4lIGFycmFuZ2UocmVzdWx0X21lZGlvX2NsYXMpICANCg0KDQojbnVtZXJvIGRlIHZ1ZWx0YXMgbGlkZXJhbmRvDQoNCg0KI3B1bnRvcyBwb3IgY2FycmVyYSAocHVudG9zL2NhcnJlcmEpDQoNCnB1bnRvc194X2NhcnJlcmEgPC0gIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCAgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIHBvaW50cykgJT4lIGZ1bGxfam9pbiguLCBuX2NhcnJlcmFzLCAgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgZ3JvdXBfYnkoZHJpdmVySWQpICU+JSBtdXRhdGUodG90YWxfcHVudG9zID0gc3VtKHBvaW50cykpICU+JSBkaXN0aW5jdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBudW1lcm9fY2FycmVyYXMsIHRvdGFsX3B1bnRvcykgJT4lIG11dGF0ZShtZWRpYV9wdW50b3MgPSB0b3RhbF9wdW50b3MvbnVtZXJvX2NhcnJlcmFzKSAlPiUgYXJyYW5nZShkZXNjKG1lZGlhX3B1bnRvcykpDQoNCm9iamV0b3Nfbm9fYm9ycmFyIDwtIGMoInZpY3Rvcmlhc19jb25fbm9tYnJlIiwgIm5fY2FycmVyYXNfbm9tIiwgIm1hc192aWN0b3JpYXMiKQ0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBvYmpldG9zX25vX2JvcnJhcl0pDQpgYGANCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCg0KI3NlIG5lY2VzaXRhICJtYXNfdmljdG9yaWFzIg0KDQoNCmdnX21hc192aWN0b3JpYXMgPC0gZ2dwbG90KG1hc192aWN0b3JpYXMsIGFlcyh4ID0gcmVvcmRlcihkcml2ZXJSZWYsIG5fdmljdG9yaWFzKSwgeSA9IG5fdmljdG9yaWFzICkpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgbGFicyh4ID0gIlBpbG90byIgLCB5ID0gIk7Dum1lcm8gZGUgdmljdG9yaWFzIikNCmdnX21hc192aWN0b3JpYXMNCiN0cmFiYWpvIC0tPiBkYXJsZSBmb3JtYXRvIGNodWxvLCB5YSB2ZXJlbW9zIGVzdGUgcHVlbnRlIHNpIGxlIHBvZGVtb3MgbWV0ZXIgZGluw6FtaWNvIG8gcXVlDQoNCm9iamV0b3Nfbm9fYm9ycmFyIDwtIGMoInZpY3Rvcmlhc19jb25fbm9tYnJlIiwgIm5fY2FycmVyYXNfbm9tIikNCnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgb2JqZXRvc19ub19ib3JyYXJdKSAgICAgICAgICAgICAgICAgICAgICAgDQpgYGANCg0KDQojIyBFc3Bhw7FvbGVzIHBvciBsYSBGMQ0KDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiNwaWxvdG9zIGVzcGHDsW9sZXMNCg0KcGlsb3RvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvZHJpdmVycy5jc3YiKQ0KDQpwaWxvdG9zX2VzcCA8LSBwaWxvdG9zICU+JSBmaWx0ZXIobmF0aW9uYWxpdHkgPT0gIlNwYW5pc2giKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG5hdGlvbmFsaXR5KSANCg0KDQojbWFzIHZpY3RvcmlhcyBkZSBwaWxvdG9zIGVzcGHDsW9sZXMNCm1hc192aWN0b3JpYXNfZXNwIDwtIGZ1bGxfam9pbih2aWN0b3JpYXNfY29uX25vbWJyZSwgcGlsb3RvcywgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgZmlsdGVyKG5hdGlvbmFsaXR5LnggPT0gIlNwYW5pc2giKSAlPiUgIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLngsIG5fdmljdG9yaWFzLCBuYXRpb25hbGl0eS54KSANCg0Kb2JqZXRvc19ub19ib3JyYXIgPC0gYygidmljdG9yaWFzX2Nvbl9ub21icmUiLCAibl9jYXJyZXJhc19ub20iLCAibWFzX3ZpY3RvcmlhcyIpDQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpgYGANCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRSwgaW5jbHVkZSA9IEZBTFNFfQ0KI2RhdG9zIGRlIGVzY3VkZXJpYXMgcGEgcXVpZW4gcXVpZXJhIGhhY2VyIGFsZ28NCmVzY3VkZXJpYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NvbnN0cnVjdG9ycy5jc3YiKQ0KZXNjdWRlcmlhczIgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NvbnN0cnVjdG9yX3N0YW5kaW5ncy5jc3YiKQ0KcmVzdWx0X2VzY3VkZXJpYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NvbnN0cnVjdG9yX3Jlc3VsdHMuY3N2IikNCg0KcGlsb3RvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvZHJpdmVycy5jc3YiKQ0KcmVzdWx0YWRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmVzdWx0cy5jc3YiKQ0KY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQojZXNjdWRlcmlhc2VzcCA8LSBlc2N1ZGVyaWFzICU+JSBmaWx0ZXIobmF0aW9uYWxpdHkgPT0gIlNwYW5pc2giKSAjZXNjdWRlcmlhcyBlc3Bhw7FvbGFzDQoNCmNhbXBlb25lc19lc2MgPC0gZnVsbF9qb2luKHBpbG90b3MsIHJlc3VsdGFkb3MsIGMoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgZnVsbF9qb2luKC4sIGNhcnJlcmFzLCBjKCJyYWNlSWQiID0gInJhY2VJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG5hdGlvbmFsaXR5LCBjb25zdHJ1Y3RvcklkLCBwb2ludHMsIHllYXIsIHJvdW5kKSAlPiUgZnVsbF9qb2luKC4sIGVzY3VkZXJpYXMsIGMoImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBuYXRpb25hbGl0eS54LCBjb25zdHJ1Y3RvcklkLCBwb2ludHMsIHllYXIsIG5hbWUsIHJvdW5kKSAlPiUgIGdyb3VwX2J5KHllYXIsIGRyaXZlclJlZikgJT4lICBtdXRhdGUocHVudG9zX3RvdGFsZXMgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKSAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHNsaWNlX21heChwdW50b3NfdG90YWxlcywgbj0xKSAlPiUgc2VsZWN0KG5hbWUsIGRyaXZlclJlZikgJT4lIGdyb3VwX2J5KG5hbWUsIGRyaXZlclJlZikgJT4lIG11dGF0ZSh0b3RhbF9jYW1wID0gc3VtKCBOTiA9IG4oKSkpICU+JSBhcnJhbmdlKG5hbWUpIA0KDQoNCmNhbXBlb25lc19lc2MgPC0gY2FtcGVvbmVzX2VzY1shKGNhbXBlb25lc19lc2MkZHJpdmVyUmVmID09ICdtYXhfdmVyc3RhcHBlbicpLF0gDQoNCmxpYnJhcnkodHJlZW1hcCkNCmxpYnJhcnkoZDN0cmVlUikNCg0KDQojIGJhc2ljIHRyZWVtYXANCmdnX2VzY19jYW1wZW9uZXMgPC0gdHJlZW1hcChjYW1wZW9uZXNfZXNjLA0KICAgICAgICAgICAgaW5kZXg9YygibmFtZSIsImRyaXZlclJlZiIpLA0KICAgICAgICAgICAgdlNpemU9InRvdGFsX2NhbXAiLA0KICAgICAgICAgICAgdHlwZT0iaW5kZXgiLA0KICAgICAgICAgICAgdkNvbG9yID0gIm5hbWUiLA0KICAgICAgICAgICAgcGFsZXR0ZSA9ICJTZXQyIiwNCiAgICAgICAgICAgIGFsaWduLmxhYmVscz1saXN0KA0KICAgICAgICAgICAgICBjKCJjZW50ZXIiLCAiY2VudGVyIiksIA0KICAgICAgICAgICAgICBjKCJjZW50ZXIiLCAiYm90dG9tIikpLA0KICAgICAgICAgICAgdGl0bGUgPSAiRXNjdWRlcsOtYXMgY29uIG3DoXMgY2FtcGVvbmVzIiwNCiAgICAgICAgICAgIHRpdGxlLmxlZ2VuZCA9ICJFc2N1ZGVyw61hcyIpICAgDQoNCmBgYA0KDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFfQ0KI2RhdG9zIGRlIGVzY3VkZXJpYXMgcGEgcXVpZW4gcXVpZXJhIGhhY2VyIGFsZ28NCiNlc2N1ZGVyaWFzIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9jb25zdHJ1Y3RvcnMuY3N2IikNCiNlc2N1ZGVyaWFzMiA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY29uc3RydWN0b3Jfc3RhbmRpbmdzLmNzdiIpDQojcmVzdWx0X2VzY3VkZXJpYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NvbnN0cnVjdG9yX3Jlc3VsdHMuY3N2IikNCg0KI3BpbG90b3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2RyaXZlcnMuY3N2IikNCiNyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQojY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQojZXNjdWRlcmlhc2VzcCA8LSBlc2N1ZGVyaWFzICU+JSBmaWx0ZXIobmF0aW9uYWxpdHkgPT0gIlNwYW5pc2giKSAjZXNjdWRlcmlhcyBlc3Bhw7FvbGFzDQoNCiNjYW1wZW9uZXNfZXNjIDwtIGZ1bGxfam9pbihwaWxvdG9zLCByZXN1bHRhZG9zLCBjKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lIGZ1bGxfam9pbiguLCBjYXJyZXJhcywgYygicmFjZUlkIiA9ICJyYWNlSWQiKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBuYXRpb25hbGl0eSwgY29uc3RydWN0b3JJZCwgcG9pbnRzLCB5ZWFyLCByb3VuZCkgJT4lIGZ1bGxfam9pbiguLCBlc2N1ZGVyaWFzLCBjKCJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIikpICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbmF0aW9uYWxpdHkueCwgY29uc3RydWN0b3JJZCwgcG9pbnRzLCB5ZWFyLCBuYW1lLCByb3VuZCkgJT4lICBncm91cF9ieSh5ZWFyLCBkcml2ZXJSZWYpICU+JSAgbXV0YXRlKHB1bnRvc190b3RhbGVzID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KHllYXIpICU+JSBzbGljZV9tYXgocHVudG9zX3RvdGFsZXMsIG49MSkgJT4lIHNlbGVjdChuYW1lLCBkcml2ZXJSZWYpICU+JSBncm91cF9ieShuYW1lLCBkcml2ZXJSZWYpICU+JSBtdXRhdGUodG90YWxfY2FtcCA9IHN1bSggTk4gPSBuKCkpKSAlPiUgYXJyYW5nZShuYW1lKSANCg0KDQojY2FtcGVvbmVzX2VzYyA8LSBjYW1wZW9uZXNfZXNjWyEoY2FtcGVvbmVzX2VzYyRkcml2ZXJSZWYgPT0gJ21heF92ZXJzdGFwcGVuJyksXSANCg0KI2xpYnJhcnkodHJlZW1hcCkNCiNsaWJyYXJ5KGQzdHJlZVIpDQoNCg0KIyBiYXNpYyB0cmVlbWFwDQojZ2dfZXNjX2NhbXBlb25lcyA8LSB0cmVlbWFwKGNhbXBlb25lc19lc2MsDQogICAgICAgICAgICAjaW5kZXg9YygibmFtZSIsImRyaXZlclJlZiIpLA0KICAgICAgICAgICAgI3ZTaXplPSJ0b3RhbF9jYW1wIiwNCiAgICAgICAgICAgICN0eXBlPSJpbmRleCIsDQogICAgICAgICAgICAjdkNvbG9yID0gIm5hbWUiLA0KICAgICAgICAgICAgI3BhbGV0dGUgPSAiU2V0MiIsDQogICAgICAgICAgICAjYWxpZ24ubGFiZWxzPWxpc3QoDQogICAgICAgICAgICAgICNjKCJjZW50ZXIiLCAiY2VudGVyIiksIA0KICAgICAgICAgICAgICAjYygiY2VudGVyIiwgImJvdHRvbSIpKSwNCiAgICAgICAgICAgICN0aXRsZSA9ICJFc2N1ZGVyw61hcyBjb24gbcOhcyBjYW1wZW9uZXMiLA0KICAgICAgICAgICAgI3RpdGxlLmxlZ2VuZCA9ICJFc2N1ZGVyw61hcyIpICAgDQoNCmBgYA0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KDQppbnRlcl9jYW1wIDwtIGQzdHJlZTIoZ2dfZXNjX2NhbXBlb25lcyAsICByb290bmFtZSA9ICJFc2N1ZGVyw61hcyB5IENhbXBlb25lcyIgKQ0KaW50ZXJfY2FtcA0KDQoNCmBgYA0KDQojIEFsb25zbyAoZWwgbmFubykNCmBgYHtyLCBldmFsID0gVFJVRSwgZWNobyA9IFRSVUV9DQojYWxvbnNvIHZzIGNvbXBhw7Flcm9zIGRlIGVxdWlwbw0KDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQpyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQplc2N1ZGVyaWFzIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9jb25zdHJ1Y3RvcnMuY3N2IikNCmNhcnJlcmFzIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yYWNlcy5jc3YiKQ0KDQphbG92c2FsbCA8LSBmdWxsX2pvaW4ocGlsb3RvcywgcmVzdWx0YWRvcywgYyAoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgIHNlbGVjdChkcml2ZXJSZWYsIHJlc3VsdElkLCByYWNlSWQsIGNvbnN0cnVjdG9ySWQsIHBvc2l0aW9uLCBwb2ludHMpICU+JSBmdWxsX2pvaW4oLiwgZXNjdWRlcmlhcywgYyAoImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiKSkgJT4lIHNlbGVjdChkcml2ZXJSZWYsIHJlc3VsdElkLCByYWNlSWQsIGNvbnN0cnVjdG9ySWQsIHBvc2l0aW9uLCBwb3NpdGlvbiwgcG9pbnRzLCBuYW1lKSAlPiUgZnVsbF9qb2luKC4sIGNhcnJlcmFzLCBjICgicmFjZUlkIiA9ICJyYWNlSWQiKSkgJT4lIHNlbGVjdChkcml2ZXJSZWYsIHJlc3VsdElkLCByYWNlSWQsIGNvbnN0cnVjdG9ySWQsIHBvc2l0aW9uLCBwb3NpdGlvbiwgcG9pbnRzLCBuYW1lLngsIHllYXIsIHJvdW5kKQ0KDQojYWxvX3ZzX21hcnF1ZXMgPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyID09IDIwMDEsIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJtYXJxdWVzIiksIHJvdW5kIDw9IDE0KQ0KDQphbG9fdnNfdHJ1bGxpIDwtIGFsb3ZzYWxsICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAwMywgMjAwNCksIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJ0cnVsbGkiKSkgJT4lIHNsaWNlKDE6MTUsIDE3OjY3KSAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmLCB5ZWFyKSAlPiUgbXV0YXRlKHB1bnRvc19hY3VtdWxhZG9zID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkNCg0KYWxvX3ZzX2Zpc2ljaGVsbGEgPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDA1LCAyMDA2KSwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgImZpc2ljaGVsbGEiKSkgICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQphbG9fdnNfaGFtaWx0b24gPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyICVpbiUgYygyMDA3KSAsZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgImhhbWlsdG9uIikpICAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmLCB5ZWFyKSAlPiUgbXV0YXRlKHB1bnRvc19hY3VtdWxhZG9zID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkNCg0KYWxvX3ZzX3BpcXVldCA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgJWluJSBjKDIwMDgsIDIwMDkpLCBkcml2ZXJSZWYgJWluJSBjKCJhbG9uc28iLCAicGlxdWV0X2pyIikpICU+JSBzbGljZSgxOjI4LCAzNjo2MykgJT4lIGdyb3VwX2J5KGRyaXZlclJlZiwgeWVhcikgJT4lIG11dGF0ZShwdW50b3NfYWN1bXVsYWRvcyA9IGN1bXN1bShwb2ludHMpKSAlPiUgdW5ncm91cCgpDQoNCiNhbG9fdnNfZ3Jvc2plYW4gPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyID09IDIwMDksIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJncm9zamVhbiIpLCByb3VuZCA+PSAxMSkNCg0KYWxvX3ZzX21hc3NhIDwtIGFsb3ZzYWxsICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAxMCwgMjAxMSwgMjAxMyksIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJtYXNzYSIpKSAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmLCB5ZWFyKSAlPiUgbXV0YXRlKHB1bnRvc19hY3VtdWxhZG9zID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkNCg0KYWxvX3ZzX3JhaWtrb25lbiA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgPT0gMjAxNCwgZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgInJhaWtrb25lbiIpKSAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmLCB5ZWFyKSAlPiUgbXV0YXRlKHB1bnRvc19hY3VtdWxhZG9zID0gY3Vtc3VtKHBvaW50cykpICU+JSB1bmdyb3VwKCkNCg0KYWxvX3ZzX2J1dHRvbiA8LSBhbG92c2FsbCAlPiUgZmlsdGVyKHllYXIgJWluJSBjKDIwMTUsIDIwMTYpLCBkcml2ZXJSZWYgJWluJSBjKCJhbG9uc28iLCAiYnV0dG9uIikpICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQphbG9fdnNfdmFuZG9vcm5lIDwtIGFsb3ZzYWxsICU+JSBmaWx0ZXIoeWVhciAlaW4lIGMoMjAxNywgMjAxOCksIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJ2YW5kb29ybmUiKSkgJT4lIHNsaWNlKDE6NDUsIDQ3OjgxKSAlPiUgZ3JvdXBfYnkoZHJpdmVyUmVmLCB5ZWFyKSAlPiUgbXV0YXRlKHB1bnRvc19hY3VtdWxhZG9zID0gY3Vtc3VtKHBvaW50cykpJT4lIHVuZ3JvdXAoKSANCg0KYWxvX3ZzX29jb24gPC0gYWxvdnNhbGwgJT4lIGZpbHRlcih5ZWFyID09IDIwMjEsIGRyaXZlclJlZiAlaW4lIGMoImFsb25zbyIsICJvY29uIikpICU+JSBncm91cF9ieShkcml2ZXJSZWYsIHllYXIpICU+JSBtdXRhdGUocHVudG9zX2FjdW11bGFkb3MgPSBjdW1zdW0ocG9pbnRzKSkgJT4lIHVuZ3JvdXAoKQ0KDQpBTE9fVlNfQUxMIDwtIGZ1bGxfam9pbihhbG9fdnNfdHJ1bGxpLCBhbG9fdnNfZmlzaWNoZWxsYSwgYygiZHJpdmVyUmVmIj0gImRyaXZlclJlZiIsICJyZXN1bHRJZCIgPSAicmVzdWx0SWQiLCAicmFjZUlkIiA9ICJyYWNlSWQiLCAiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIsICJwb3NpdGlvbiIgPSAicG9zaXRpb24iLCAicG9pbnRzIiA9ICJwb2ludHMiLCAibmFtZS54IiA9ICJuYW1lLngiLCAieWVhciIgPSAieWVhciIsICJyb3VuZCIgPSAicm91bmQiICwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKSAlPiUgDQogIGZ1bGxfam9pbiguLCBhbG9fdnNfaGFtaWx0b24sIGMoImRyaXZlclJlZiI9ICJkcml2ZXJSZWYiLCAicmVzdWx0SWQiID0gInJlc3VsdElkIiwgInJhY2VJZCIgPSAicmFjZUlkIiwgImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiLCAicG9zaXRpb24iID0gInBvc2l0aW9uIiwgInBvaW50cyIgPSAicG9pbnRzIiwgIm5hbWUueCIgPSAibmFtZS54IiwgInllYXIiID0gInllYXIiLCAicm91bmQiID0gInJvdW5kIiwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKSAlPiUgDQogIGZ1bGxfam9pbiguLCBhbG9fdnNfcGlxdWV0LCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIiwgInJlc3VsdElkIiA9ICJyZXN1bHRJZCIsICJyYWNlSWQiID0gInJhY2VJZCIsICJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIiwgInBvc2l0aW9uIiA9ICJwb3NpdGlvbiIsICJwb2ludHMiID0gInBvaW50cyIsICJuYW1lLngiID0gIm5hbWUueCIsICJ5ZWFyIiA9ICJ5ZWFyIiwgInJvdW5kIiA9ICJyb3VuZCIsICJwdW50b3NfYWN1bXVsYWRvcyIgPSAicHVudG9zX2FjdW11bGFkb3MiKSkgJT4lIA0KICBmdWxsX2pvaW4oLiwgYWxvX3ZzX21hc3NhLCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIiwgInJlc3VsdElkIiA9ICJyZXN1bHRJZCIsICJyYWNlSWQiID0gInJhY2VJZCIsICJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIiwgInBvc2l0aW9uIiA9ICJwb3NpdGlvbiIsICJwb2ludHMiID0gInBvaW50cyIsICJuYW1lLngiID0gIm5hbWUueCIsICJ5ZWFyIiA9ICJ5ZWFyIiwgInJvdW5kIiA9ICJyb3VuZCIsICJwdW50b3NfYWN1bXVsYWRvcyIgPSAicHVudG9zX2FjdW11bGFkb3MiKSkgICU+JSANCiAgZnVsbF9qb2luKC4sIGFsb192c19yYWlra29uZW4sIGMoImRyaXZlclJlZiI9ICJkcml2ZXJSZWYiLCAicmVzdWx0SWQiID0gInJlc3VsdElkIiwgInJhY2VJZCIgPSAicmFjZUlkIiwgImNvbnN0cnVjdG9ySWQiID0gImNvbnN0cnVjdG9ySWQiLCAicG9zaXRpb24iID0gInBvc2l0aW9uIiwgInBvaW50cyIgPSAicG9pbnRzIiwgIm5hbWUueCIgPSAibmFtZS54IiwgInllYXIiID0gInllYXIiLCAicm91bmQiID0gInJvdW5kIiwgInB1bnRvc19hY3VtdWxhZG9zIiA9ICJwdW50b3NfYWN1bXVsYWRvcyIpKSAlPiUgDQogIGZ1bGxfam9pbiguLCBhbG9fdnNfYnV0dG9uLCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIiwgInJlc3VsdElkIiA9ICJyZXN1bHRJZCIsICJyYWNlSWQiID0gInJhY2VJZCIsICJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIiwgInBvc2l0aW9uIiA9ICJwb3NpdGlvbiIsICJwb2ludHMiID0gInBvaW50cyIsICJuYW1lLngiID0gIm5hbWUueCIsICJ5ZWFyIiA9ICJ5ZWFyIiwgInJvdW5kIiA9ICJyb3VuZCIsICJwdW50b3NfYWN1bXVsYWRvcyIgPSAicHVudG9zX2FjdW11bGFkb3MiKSkgJT4lIA0KICBmdWxsX2pvaW4oLiwgYWxvX3ZzX3ZhbmRvb3JuZSwgYygiZHJpdmVyUmVmIj0gImRyaXZlclJlZiIsICJyZXN1bHRJZCIgPSAicmVzdWx0SWQiLCAicmFjZUlkIiA9ICJyYWNlSWQiLCAiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIsICJwb3NpdGlvbiIgPSAicG9zaXRpb24iLCAicG9pbnRzIiA9ICJwb2ludHMiLCAibmFtZS54IiA9ICJuYW1lLngiLCAieWVhciIgPSAieWVhciIsICJyb3VuZCIgPSAicm91bmQiLCAicHVudG9zX2FjdW11bGFkb3MiID0gInB1bnRvc19hY3VtdWxhZG9zIikpICU+JSANCiAgZnVsbF9qb2luKC4sIGFsb192c19vY29uLCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIiwgInJlc3VsdElkIiA9ICJyZXN1bHRJZCIsICJyYWNlSWQiID0gInJhY2VJZCIsICJjb25zdHJ1Y3RvcklkIiA9ICJjb25zdHJ1Y3RvcklkIiwgInBvc2l0aW9uIiA9ICJwb3NpdGlvbiIsICJwb2ludHMiID0gInBvaW50cyIsICJuYW1lLngiID0gIm5hbWUueCIsICJ5ZWFyIiA9ICJ5ZWFyIiwgInJvdW5kIiA9ICJyb3VuZCIsICJwdW50b3NfYWN1bXVsYWRvcyIgPSAicHVudG9zX2FjdW11bGFkb3MiKSkNCg0Kb2JqZXRvc19ub19ib3JyYXIgPC0gYygiQUxPX1ZTX0FMTCIsICJuX2NhcnJlcmFzX25vbSIsICJ2aWN0b3JpYXNfY29uX25vbWJyZSIpDQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkNCg0KDQpnYygpICNpbnN0cnVjY2lvbiBwYXJhIHF1ZSBjYXJndWUgZWwgZ3JhZmljbywgYWwgc2VyIHRhbiBjb21wbGVqbyBkYSBlcnJvciBkZSBubyBzw6kgcXXDqSBwZXJvIGNvbiBlc3RvIGZ1bmNpb25hDQpnZ2Fsb192c19hbGwgPC0gZ2dwbG90KGRhdGEgPSBBTE9fVlNfQUxMLCBhZXMocm91bmQsIHB1bnRvc19hY3VtdWxhZG9zLCBjb2xvciA9IGRyaXZlclJlZikpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgbGFicyh0aXRsZSA9ICJBbG9uc28gY29udHJhIGVsIG11bmRvIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJsZSBkYXMgdW4gY2FydG9uIGNvbiBydWVkYXMgeSBhw7puIHRlIHNhY2EgcHVudG9zIiwNCiAgICAgICB5ID0gIlB1bnRvcyIsIHggPSAiIikgKyBmYWNldF93cmFwKCB+IHllYXIpICsgdHJhbnNpdGlvbl9yZXZlYWwocm91bmQpDQoNCiNnZ2Fsb192c19hbGwNCg0Kb2JqZXRvc19ub19ib3JyYXIgPC0gYygidmljdG9yaWFzX2Nvbl9ub21icmUiLCAibl9jYXJyZXJhc19ub20iLCAibWFzX3ZpY3RvcmlhcyIpDQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkNCmBgYA0KDQojIENhbXBlb25lcyBkZWwgbXVuZG8NCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCg0KcGlsb3RvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvZHJpdmVycy5jc3YiKQ0KcmVzdWx0YWRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmVzdWx0cy5jc3YiKQ0KZXNjdWRlcmlhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY29uc3RydWN0b3JzLmNzdiIpDQpjYXJyZXJhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmFjZXMuY3N2IikNCg0KDQoNCmNhbXBlb25lcyA8LSBmdWxsX2pvaW4ocGlsb3RvcywgcmVzdWx0YWRvcywgYygiZHJpdmVySWQiID0gImRyaXZlcklkIikpICU+JSBmdWxsX2pvaW4oLiwgY2FycmVyYXMsIGMoInJhY2VJZCIgPSAicmFjZUlkIikpICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZiwgbmF0aW9uYWxpdHksIGNvbnN0cnVjdG9ySWQsIHBvaW50cywgeWVhciwgcm91bmQpICU+JSBmdWxsX2pvaW4oLiwgZXNjdWRlcmlhcywgYygiY29uc3RydWN0b3JJZCIgPSAiY29uc3RydWN0b3JJZCIpKSAlPiUgc2VsZWN0KGRyaXZlcklkLCBkcml2ZXJSZWYsIG5hdGlvbmFsaXR5LngsIGNvbnN0cnVjdG9ySWQsIHBvaW50cywgeWVhciwgbmFtZSwgcm91bmQpICU+JSAgZ3JvdXBfYnkoeWVhciwgZHJpdmVyUmVmKSAlPiUgIG11dGF0ZShwdW50b3NfdG90YWxlcyA9IGN1bXN1bShwb2ludHMpKSAlPiUgdW5ncm91cCgpICU+JSBncm91cF9ieSh5ZWFyKSAlPiUgc2xpY2VfbWF4KHB1bnRvc190b3RhbGVzLCBuPTEpICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KGRyaXZlclJlZiklPiUgbXV0YXRlKHRvdGFsX2NhbXBlb25hdG9zID0gc3VtKE5OID0gbigpKSkgJT4lIGRpc3RpbmN0KGRyaXZlclJlZiwgbmF0aW9uYWxpdHkueCwgdG90YWxfY2FtcGVvbmF0b3MpICU+JSBhcnJhbmdlKG5hdGlvbmFsaXR5LngsIHRvdGFsX2NhbXBlb25hdG9zKSAjbWUgcXVlZG8gcG9yIGFxdWkNCiAgDQpjYW1wZW9uZXMgPC0gY2FtcGVvbmVzWyEoY2FtcGVvbmVzJGRyaXZlclJlZiA9PSAnbWF4X3ZlcnN0YXBwZW4nKSxdDQppZCA8LSByb3duYW1lcyhjYW1wZW9uZXMpDQpjYW1wZW9uZXMgPC0gY2JpbmQoaWQ9aWQsIGNhbXBlb25lcykNCmNhbXBlb25lc1ssIGMoMSldIDwtIHNhcHBseShjYW1wZW9uZXNbLCBjKDEpXSwgYXMubnVtZXJpYykNCg0KDQoNCg0KbGFiZWxfY2FtcGVvbmVzIDwtIGNhbXBlb25lcw0KbnVtYmVyX29mX2JhciA8LSBucm93KGxhYmVsX2NhbXBlb25lcykNCg0KYW5nbGUgPC0gOTAgLSAzNjAgKiAobGFiZWxfY2FtcGVvbmVzJGlkLTAuNSkgL251bWJlcl9vZl9iYXIgICAgICMgSSBzdWJzdHJhY3QgMC41IGJlY2F1c2UgdGhlIGxldHRlciBtdXN0IGhhdmUgdGhlIGFuZ2xlIG9mIHRoZSBjZW50ZXIgb2YgdGhlIGJhcnMuIE5vdCBleHRyZW1lIHJpZ2h0KDEpIG9yIGV4dHJlbWUgbGVmdCAoMCkNCmxhYmVsX2NhbXBlb25lcyRoanVzdCA8LSBpZmVsc2UoIGFuZ2xlIDwgLTkwLCAxLCAwKQ0KbGFiZWxfY2FtcGVvbmVzJGFuZ2xlIDwtIGlmZWxzZShhbmdsZSA8IC05MCwgYW5nbGUrMTgwLCBhbmdsZSkNCg0KDQoNCmJhc2VfY2FtcGVvbmVzIDwtIGNhbXBlb25lcyAlPiUgDQogIGdyb3VwX2J5KG5hdGlvbmFsaXR5LngpICU+JSANCiAgc3VtbWFyaXNlKHN0YXJ0PW1pbihpZCksIGVuZD1tYXgoaWQpKSAlPiUgDQogIHJvd3dpc2UoKSAlPiUgDQogIG11dGF0ZSh0aXRsZT1tZWFuKGMoc3RhcnQsIGVuZCkpKQ0KDQpncmlkX2NhbXBlb25lcyA8LSBiYXNlX2NhbXBlb25lcw0KZ3JpZF9jYW1wZW9uZXMkZW5kIDwtIGdyaWRfY2FtcGVvbmVzJGVuZFsgYyggbnJvdyhncmlkX2NhbXBlb25lcyksIDE6bnJvdyhncmlkX2NhbXBlb25lcyktMSldICsgMQ0KZ3JpZF9jYW1wZW9uZXMkc3RhcnQgPC0gZ3JpZF9jYW1wZW9uZXMkc3RhcnQgLSAxDQpncmlkX2NhbXBlb25lcyA8LSBncmlkX2NhbXBlb25lc1stMSxdDQoNCnAgPC0gZ2dwbG90KGNhbXBlb25lcywgYWVzKHg9YXMuZmFjdG9yKHllYXIpLCB5PXRvdGFsX2NhbXBlb25hdG9zLCBmaWxsPW5hdGlvbmFsaXR5LngsIGNvbG9yID0gbmF0aW9uYWxpdHkueCkpICsgZ2VvbV9iYXIoYWVzKHg9YXMuZmFjdG9yKGlkKSwgeT10b3RhbF9jYW1wZW9uYXRvcywgZmlsbD1uYXRpb25hbGl0eS54KSwgc3RhdD0iaWRlbnRpdHkiLCBhbHBoYT0wLjUpICsjIEFkZCBhIHZhbD0xMDAvNzUvNTAvMjUgbGluZXMuIEkgZG8gaXQgYXQgdGhlIGJlZ2lubmluZyB0byBtYWtlIHN1ciBiYXJwbG90cyBhcmUgT1ZFUiBpdC4NCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMCwgeSA9IDgsIHhlbmQgPSAzMSwgeWVuZCA9IDgpLCBjb2xvdXIgPSAiZ3JleSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMCwgeSA9IDYsIHhlbmQgPSAzMSwgeWVuZCA9IDYpLCBjb2xvdXIgPSAiZ3JleSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMCwgeSA9IDQsIHhlbmQgPSAzMSwgeWVuZCA9IDQpLCBjb2xvdXIgPSAiZ3JleSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMCwgeSA9IDIsIHhlbmQgPSAzMSwgeWVuZCA9IDIpLCBjb2xvdXIgPSAiZ3JleSIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgDQogICAjIEFkZCB0ZXh0IHNob3dpbmcgdGhlIHZhbHVlIG9mIGVhY2ggMTAwLzc1LzUwLzI1IGxpbmVzDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IHJlcChtYXgoY2FtcGVvbmVzJGlkKSw0KSwgeSA9IGMoMiwgNCwgNiwgOCksIGxhYmVsID0gYygiMiIsICI0IiwgIjYiLCAiOCIpICwgY29sb3I9ImdyZXkiLCBzaXplPTMgLCBhbmdsZT0wLCBmb250ZmFjZT0iYm9sZCIsIGhqdXN0PTEpICsNCiAgDQogICBnZW9tX2JhcihhZXMoeD1hcy5mYWN0b3IoaWQpLCB5PXRvdGFsX2NhbXBlb25hdG9zLCBmaWxsPW5hdGlvbmFsaXR5LngpLCBzdGF0PSJpZGVudGl0eSIsIGFscGhhPTAuNSkgKw0KICB5bGltKC0xMCwyMSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90Lm1hcmdpbiA9IHVuaXQocmVwKC0xLDQpLCAiY20iKSApICsNCiAgY29vcmRfcG9sYXIoKSArIA0KICBnZW9tX3RleHQoZGF0YT1sYWJlbF9jYW1wZW9uZXMsIGFlcyh4PWlkLCB5PTEwLCBsYWJlbD1kcml2ZXJSZWYsIGhqdXN0PWhqdXN0KSwgY29sb3I9ImJsYWNrIiwgZm9udGZhY2U9ImJvbGQiLGFscGhhPTAuNiwgc2l6ZT0yLjUsIGFuZ2xlPSBsYWJlbF9jYW1wZW9uZXMkYW5nbGUsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIA0KICAgIyBBZGQgYmFzZSBsaW5lIGluZm9ybWF0aW9uDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDAuNzAsIHkgPSAtMSwgeGVuZCA9IDIuNDUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJibGFjayIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDIuNiwgeSA9IC0xLCB4ZW5kID0gMy41NSwgeWVuZCA9IC0xKSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAzLjY1LCB5ID0gLTEsIHhlbmQgPSA1LjQ1LCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDUuNTUsIHkgPSAtMSwgeGVuZCA9IDcuMzUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJibGFjayIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gNy41LCB5ID0gLTEsIHhlbmQgPSAxMC41MCwgeWVuZCA9IC0xKSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAxMC43LCB5ID0gLTEsIHhlbmQgPSAxOS4yMCwgeWVuZCA9IC0xKSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAxOS40LCB5ID0gLTEsIHhlbmQgPSAyMC4zLCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDIwLjQ1LCB5ID0gLTEsIHhlbmQgPSAyMy40LCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArIA0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAyMy42NSwgeSA9IC0xLCB4ZW5kID0gMjQuMzUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJibGFjayIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsgDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDI0LjYwLCB5ID0gLTEsIHhlbmQgPSAyNywgeWVuZCA9IC0xKSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKw0KICBnZW9tX3NlZ21lbnQoZGF0YT1ncmlkX2NhbXBlb25lcywgYWVzKHggPSAyNy4yLCB5ID0gLTEsIHhlbmQgPSAyOS41LCB5ZW5kID0gLTEpLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYT0xLCBzaXplPTAuMyAsIGluaGVyaXQuYWVzID0gRkFMU0UgKSArDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDI5LjcsIHkgPSAtMSwgeGVuZCA9IDMwLjUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJibGFjayIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsgDQogIGdlb21fc2VnbWVudChkYXRhPWdyaWRfY2FtcGVvbmVzLCBhZXMoeCA9IDMwLjcsIHkgPSAtMSwgeGVuZCA9IDMxLjUsIHllbmQgPSAtMSksIGNvbG91ciA9ICJibGFjayIsIGFscGhhPTEsIHNpemU9MC4zICwgaW5oZXJpdC5hZXMgPSBGQUxTRSApICsNCiAgZ2VvbV9zZWdtZW50KGRhdGE9Z3JpZF9jYW1wZW9uZXMsIGFlcyh4ID0gMzEuNywgeSA9IC0xLCB4ZW5kID0gMzIuNSwgeWVuZCA9IC0xKSwgY29sb3VyID0gImJsYWNrIiwgYWxwaGE9MSwgc2l6ZT0wLjMgLCBpbmhlcml0LmFlcyA9IEZBTFNFICkgKyANCiAgZ2VvbV90ZXh0KGRhdGE9YmFzZV9jYW1wZW9uZXMsIGFlcyh4ID0gdGl0bGUsIHkgPSAtMTgsIGxhYmVsPW5hdGlvbmFsaXR5LngpLCBoanVzdD1jKDEsMSwwLDApLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYT0wLjgsIHNpemU9NCwgZm9udGZhY2U9ImJvbGQiLCBpbmhlcml0LmFlcyA9IEZBTFNFKSANCiANCiNwIA0KDQoNCiAgIA0KDQpgYGANCg0KDQojIFRlbXBsb3MgeSB0aWVtcG9zDQpgYGB7ciwgZXZhbCA9IFRSVUUsIGVjaG8gPSBUUlVFfQ0KDQpjYXJyZXJhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvcmFjZXMuY3N2IikNCmNpcmN1aXRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY2lyY3VpdHMuY3N2IikNCg0KY2FycmVyYXNfMjEgPC0gZnVsbF9qb2luKGNhcnJlcmFzLGNpcmN1aXRvcywgYygiY2lyY3VpdElkIiA9ICJjaXJjdWl0SWQiKSkgJT4lDQogIGZpbHRlcih5ZWFyPT0iMjAyMCIpICU+JQ0KICBzZWxlY3Qocm91bmQsIG5hbWUueCwgbmFtZS55LCBkYXRlLCBsb2NhdGlvbixjb3VudHJ5LCBsYXQsIGxuZywgYWx0KSAlPiUNCiAgYXJyYW5nZShyb3VuZCkgJT4lIA0KICBtdXRhdGUocm91bmQyID0gcm91bmQpIA0KDQpjYXJyZXJhc18yMV92MiA8LSBjYXJyZXJhc18yMVssIGMoMSwgNCwgMTAsIDIsIDMsIDUsIDYsIDcsIDgsIDkpXQ0KY2FycmVyYXNfMjFfdjIgPC0gY2FycmVyYXNfMjFfdjIlPiUgIHVuaXRlKC4gLHZhcmlhYmxlcywgYygxLCA1LCA3KSwgc2VwID0gIjsgIikNCg0KI3BydWViYXMgcGFyYSBtYXBhcywgbm8gZWplY3V0YXIgZGUgbW9tZW50bw0KI2xpYnJhcnkod2lkZ2V0ZnJhbWUpDQojbGlicmFyeShsZWFmbGV0KQ0KI2wgPC0gbGVhZmxldCgpICU+JSBzZXRWaWV3KGxhdCA9IDQ1LjYxNTYwLCBsbmcgPSA5LjI4MTExMCwgem9vbT0xKQ0KI2ZyYW1lV2lkZ2V0KGwpIA0KDQojbWFwYUNpdWRhZHlQdWVibG9tYXlvcmluY2lBY3UgPC0gbGVhZmxldCgpICU+JQ0KICMgc2V0VmlldyhsbmcgPSAtMC4yNDM1OTEsIGxhdCA9IDM4LjgyMSwgem9vbSA9IDcpICU+JSANCiAgI2FkZE1hcmtlcnMobG5nID0gLTAuMjQzNTkxLCBsYXQgPSAzOC44MjEgLCBwb3B1cCA9ICJWYWxsIGRlIEdhbGxpbmVyYSIpJT4lDQogICNzZXRWaWV3KGxuZyA9IC0wLjQxODU5OCwgbGF0ID0gNDAuMjAxMSwgem9vbSA9IDcpICU+JSANCiAgI2FkZE1hcmtlcnMobG5nID0gLTAuNDE4NTk4LCBsYXQgPSA0MC4yMDExICwgcG9wdXAgPSAiVmlsbGFoZXJtb3NhIGRlbCByaW8iKSAlPiUgYWRkVGlsZXMoKQ0KI21hcGFDaXVkYWR5UHVlYmxvbWF5b3JpbmNpQWN1DQoNCiNNQVBBIERFTCBNVU5ETyBERSBMQSBPU1RJQSBOTyBUT0NBUiwgcG9uZ28gY29tbyBjb21lbnRhcmlvIHBhcmEgcXVlIG5vIHRhcmRlIHRhbnRvIGFsIGtuaXRlYXINCg0KDQpnbG9ib19jaXJjIDwtY3JlYXRlX2dsb2JlKCkgJT4lIGdsb2JlX3Bvdig0NS42MTU2MCwgOS4yODExMTApICU+JSBnbG9iZV9iYXJzKGNvb3JkcyhsYXQsIGxuZywgbGFiZWwgID0gdmFyaWFibGVzLCBjb2xvciA9IHJvdW5kMiksIGRhdGEgPSBjYXJyZXJhc18yMV92MikgICU+JSBzY2FsZV9iYXJzX2NvbG9yKCkNCiNnbG9ib19jaXJjDQoNCm9iamV0b3Nfbm9fYm9ycmFyIDwtIGMoInZpY3Rvcmlhc19jb25fbm9tYnJlIiwgIm5fY2FycmVyYXNfbm9tIiwgIm1hc192aWN0b3JpYXMiKQ0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBvYmpldG9zX25vX2JvcnJhcl0pDQpgYGANCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCmNpcmN1aXRvcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvY2lyY3VpdHMuY3N2IikNCnRpZW1wb3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2xhcF90aW1lcy5jc3YiKQ0KY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQoNCmNpcmN1aXRvc19ncCA8LSBmdWxsX2pvaW4oY2FycmVyYXMsIGNpcmN1aXRvcywgYygiY2lyY3VpdElkIiA9ICJjaXJjdWl0SWQiKSkgI2Fzb2NpbyBsYXMgY2FycmVyYXMgYSBzdSBjaXJjdWl0bw0KdGllbXBvc3Z1ZWx0YV94X2NhcnJlcmEgPC0gZnVsbF9qb2luKGNpcmN1aXRvc19ncCwgdGllbXBvcywgYyAoInJhY2VJZCIgPSAicmFjZUlkIikpICN0aWVtcG8gZGUgbGFzIHZ1ZWx0YXMgcG9yIGNhZGEgY2FycmVyYQ0KDQp0aWVtcG9zdnVlbHRhX3hfY2FycmVyYSA8LSBmdWxsX2pvaW4odGllbXBvc3Z1ZWx0YV94X2NhcnJlcmEsIHBpbG90b3MsIGMgKCJkcml2ZXJJZCIgPSAiZHJpdmVySWQiKSkgJT4lICBzZWxlY3QoY2lyY3VpdElkLCBuYW1lLnksIGRyaXZlcklkLCBkcml2ZXJSZWYsIHRpbWUueSwgbGFwLHBvc2l0aW9uLCB5ZWFyLCBjb3VudHJ5KSAjZnVzaW9ubyBjb24gZWwgZGYgZGUgcGlsb3RvcyBwYXJhIGFzb2NpYXIgY2FkYSB2dWVsdGEgYWwgbm9tYnJlIGRlbCBwaWxvdG8gcXVlIGxhIGhpem8NCg0KDQojY2FsY3VsbyBlbCByZWNvcmQgZGUgY2FkYSBjaXJjdWl0bywgZmlsdHJhbmRvIGVsIG1pbmltbyBkZSBsb3MgdGllbXBvcyBlbiBjYWRhIGNpcmN1aXRvDQpyZWNvcmRfZGVfY2lyY3VpdG8gPC0gdGllbXBvc3Z1ZWx0YV94X2NhcnJlcmEgJT4lIGdyb3VwX2J5KG5hbWUueSkgJT4lIHNsaWNlX21pbih0aW1lLnksIG49MSkNCg0KI251bWVybyBkZSByZWNvcmRzIGRlIGNpcmN1aXRvIHF1ZSB0aWVuZSBjYWRhIHBpbG90bw0KcmVjb3JkX3hfcGlsb3RvIDwtIHJlY29yZF9kZV9jaXJjdWl0byAlPiUgZ3JvdXBfYnkoZHJpdmVySWQpICU+JSBtdXRhdGUobnVtZXJvX3JlY29yZHMgPSBzdW0obigpKSkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBudW1lcm9fcmVjb3JkcykgJT4lIGRpc3RpbmN0KGRyaXZlclJlZiwgbnVtZXJvX3JlY29yZHMpICU+JSBhcnJhbmdlKGRlc2MobnVtZXJvX3JlY29yZHMpKQ0KDQoNCiMgbWV0ZXIgbWFwYSBkZWwgbXVuZG8gY29uIGxhIHViaWNhY2lvbiBkZSBsb3MgY2lyY3VpdG9zIGVuIGxhIHRlbXBvcmFkYSAyMDIxDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCm9iamV0b3Nfbm9fYm9ycmFyIDwtIGMoInZpY3Rvcmlhc19jb25fbm9tYnJlIiwgIm5fY2FycmVyYXNfbm9tIiwgIm1hc192aWN0b3JpYXMiKQ0Kcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBvYmpldG9zX25vX2JvcnJhcl0pDQoNCmBgYA0KDQojIENhcMOtdHVsbyBvc2N1cm8gZGVsIGRlcG9ydGUNCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCg0KI2NyZW8gZGYgZGUgbXVlcnRlcyBkZSBmb3JtdWxhIDENCm11ZXJ0ZXNmMSA8LSBkYXRhLmZyYW1lKA0KICAib3JkZW4iID0gMTo0MiwNCiAgImRyaXZlclJlZiIgPSBjKCJDaGV0IE1pbGxlciIsICJDYXJsIFNjYXJhYm9yb3VnaCIsICJPbm9mcmUgTWFyaW1vbiIsICJNYW5ueSBBeXVsbyIsICJCaWxsIFZ1a292aWNoIiwgIkFsYmVydG8gQXNjYXJpIiwiRXVnZW5pbyBDYXN0ZWxsb3R0aSIsICJLZWl0aCBBbmRyZXdzIiwgIlBhdCBPJ0Nvbm5vciIsICJMdWlnaSBNdXNzbyIsICJQZXRlciBDb2xsaW5zIiwgIlN0dWFydCBMZXdpcy1FdmFucyIsICJKZXJyeSBVbnNlciIsICJCb2IgQ29ydG5lciIsICJJdm9yIEJ1ZWIiLCAiQ2hyaXMgQnJpc3RvdyIsICJBbGFuIFN0YWNleSIsICJHaXVsaW8gQ2FiaWFuY2EiLCAiV29sZmdhbmcgdm9uIFRyaXBzIiwgIkNhcmVsIEdvZGluIGRlIEJlYXVmb3J0IiwgIkpvaG4gVGF5bG9yIiwgIkxvcmVuem8gQmFuZGluaSIsICJCb2IgQW5kZXJzb24iLCAiSm8gU2NobGVzc2VyIiwgIkdlcmhhcmQgTWl0dGVyIiwgIlBpZXJzIENvdXJhZ2UiLCAiSm9jaGVuIFJpbmR0IiwgIkpvIFNpZmZlcnQiLCAiUm9nZXIgV2lsbGlhbXNvbiIsICJGcmFuw6dvaXMgQ2V2ZXJ0IiwgIlBldGVyIFJldnNvbiIsICJIZWxtdXRoIEtvaW5pZ2ciLCAiTWFyayBEb25vaHVlIiwgIlRvbSBQcnljZSIsICJSb25uaWUgUGV0ZXJzb24iLCAiUGF0cmljayBEZXBhaWxsZXIiLCAiR2lsbGVzIFZpbGxlbmV1dmUiLCAiUmljY2FyZG8gUGFsZXR0aSIsICJFbGlvIGRlIEFuZ2VsaXMiLCAiUm9sYW5kIFJhdHplbmJlcmdlciIsICJBeXJ0b24gU2VubmEiLCAiSnVsZXMgQmlhbmNoaSIpLA0KICAibmF0aW9uYWxpdHkiID0gYygiQW1lcmljYW4iLCAiQW1lcmljYW4iLCAiQXJnZW50aW5lIiwgIkFtZXJpY2FuIiwgIkFtZXJpY2FuIiwgIkl0YWxpYW4iLCJJdGFsaWFuIiwgIkFtZXJpY2FuIiwgIkFtZXJpY2FuIiwgIkl0YWxpYW4iLCAiQnJpdGlzaCIsICJCcml0aXNoIiwgIkFtZXJpY2FuIiwgIkFtZXJpY2FuIiwgIkJyaXRpc2giLCAiQnJpdGlzaCIsICJCcml0aXNoIiwgIkl0YWxpYW4iLCAiR2VybWFuIiwgIkR1dGNoIiwgIkJyaXRpc2giLCAiSXRhbGlhbiIgLCAiQnJpdGlzaCIsICJGcmVuY2giLCAiR2VybWFuIiwgIkJyaXRpc2giLCAiQXVzdHJpYW4iLCAiU3dpc3MiLCAiQnJpdGlzaCIsICJGcmVuY2giLCAiQW1lcmljYW4iLCAiQXVzdHJpYW4iLCAiQW1lcmljYW4iLCAiQnJpdGlzaCIsICJTd2VkaXNoIiwgIkZyZW5jaCIsICJDYW5hZGlhbiIsICJJdGFsaWFuIiwgIkl0YWxpYW4iLCAiQXVzdHJpYW4iLCAiQnJhemlsaWFuIiwgIkZyZW5jaCIpLA0KICAiZG9kIiA9IGMoMTk1MywgMTk1MywgMTk1NCwgMTk1NSwgMTk1NSwgMTk1NSwgMTk1NywgMTk1NywgMTk1OCwxOTU4LDE5NTgsMTk1OCwxOTU5LDE5NTksMTk1OSwxOTYwLCAxOTYwLDE5NjEsMTk2MSwxOTY0LDE5NjYsIDE5NjcsIDE5NjcsMTk2OCwxOTY5LCAxOTcwLDE5NzAsMTk3MSwxOTczLDE5NzMsMTk3NCwxOTc0LDE5NzUsMTk3NywxOTc4LDE5ODAsMTk4MiwxOTgyLDE5ODYsMTk5NCwxOTk0LDIwMTQpKQ0KI25vIHBvbmdvIGxhcyBjb21pbGxhcyBlbiBsb3MgYcOxb3MgcGFyYSBxdWUgc2UgY3JlZW4gZGlyZWN0YW1lbnRlIGNvbW8gb2JzZXJ2YWNpb25lcyBudW1lcmljYXMNCg0KI2NyZW8gdW4gZGYgY29uIHRvZG9zIGxvcyBhw7FvcyBwYXJhIGx1ZWdvIGZ1c2lvbmFybG8sIHlhIHF1ZSBubyBoYXkgbXVlcnRlcyB0b2RvcyBsb3MgYcOxb3MgDQphbnlvcyA8LSBkYXRhLmZyYW1lKA0KICAib3JkZW4iID0gMTo3MSwNCiAgImHDsW8iID0gYygxOTUwOjIwMjApKQ0KDQojc3VtYXRvcmlvIGRlIGxhcyBtdWVydGVzIHBvciBhw7FvDQptdWVydGVzX2FueW8gPC0gbXVlcnRlc2YxICU+JSBncm91cF9ieShkb2QpICU+JSBtdXRhdGUobXVlcnRlc3hhbnlvID0gc3VtKG4oKSkpICU+JSBkaXN0aW5jdChkb2QsIG11ZXJ0ZXN4YW55bykgDQoNCiNmdXNpb25vIGxvcyAyIGRmcyBwYXJhIHF1ZSB0ZW5nYSBlbiBjdWVudGEgbG9zIGHDsW9zIGRvbmRlIG5vIGhheSBtdWVydGVzDQptdWVydGVzZjFfZmluYWwgPC0gZnVsbF9qb2luKG11ZXJ0ZXNfYW55bywgYW55b3MsIGMoImRvZCIgPSAiYcOxbyIpKSAlPiUgc2VsZWN0KGRvZCxtdWVydGVzeGFueW8pICU+JSBhcnJhbmdlKGRvZCkNCg0KI2NvbnZpZXJ0byBsb3MgTi9BIGVuIDAsIGVzIGRlY2lyLCBjdWFuZG8gbm8gaGF5IG9ic2VydmFjaW9uZXMsIGhhIGhhYmlkbyAwIG11ZXJ0ZXMNCm11ZXJ0ZXNmMV9maW5hbFtpcy5uYShtdWVydGVzZjFfZmluYWwpXSA8LSAwDQoNCiNncmFmaWNvIGRlIGxhcyBtdWVydGVzIHBvciBjYWRhIGHDsW8gKyBsYSB0ZW5kZW5jaWEgbmVnYXRpdmEgZWEgbG8gbGFyZ28gZGUgbGEgaGlzdG9yaWENCmdnX211ZXJ0ZXMgPC0gZ2dwbG90KG11ZXJ0ZXNmMV9maW5hbCwgYWVzKHggPSBkb2QsIHkgPSBtdWVydGVzeGFueW8gKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2xpbmUoKSArIGdlb21fc21vb3RoKCkgKyBsYWJzKHggPSAiQcOxbyIgLCB5ID0gIk7Dum1lcm8gZGUgbXVlcnRlcyIpDQpnZ19tdWVydGVzDQoNCg0Kb2JqZXRvc19ub19ib3JyYXIgPC0gYygidmljdG9yaWFzX2Nvbl9ub21icmUiLCAibl9jYXJyZXJhc19ub20iLCAibWFzX3ZpY3RvcmlhcyIpDQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkNCmBgYA0KDQojIFsxLiBJbnRyb2R1Y2Npw7NuXXsudmVyZGVjaXRvfQ0KDQpUZW5lbW9zIHBlbnNhZG8gZWxhYm9yYXIgZWwgdHJhYmFqbyBlbiBlcXVpcG8gc29icmUgRm9ybXVsYSAxLCB1bmEgY29tcGV0aWNpw7NuIGRlIGxhIHF1ZSBzb21vcyBtdXkgYWZpY2lvbmFkb3MsIGVudHJlIG90cmFzIGNvc2FzIHBvciBsYSBpbXBvcnRhbmNpYSBxdWUgdGllbmVuIGxvcyBkYXRvcyBhIGxhIGhvcmEgZGUgZm9ybWFsaXphciBsYXMgZXN0cmF0ZWdpYXMgZW4gbGEgY29tcGV0aWNpw7NuLiANCg0KDQojIFJlbWFuZG8gYSBjb250cmFjb3JyaWVudGUNCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCiNtYXMgcG9zaWNpb25lcyByZW1vbnRhZGFzIGVuIHVuYSBjYXJyZXJhIGdyYW4gcHJlbWlvDQoNCnRpZW1wb3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2xhcF90aW1lcy5jc3YiKQ0KY2FycmVyYXMgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL3JhY2VzLmNzdiIpDQpyZXN1bHRhZG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9yZXN1bHRzLmNzdiIpDQpjaXJjdWl0b3MgPC0gcmlvOjppbXBvcnQoZmlsZSA9ICIuL2RhdG9zL2NpcmN1aXRzLmNzdiIpDQpwaWxvdG9zIDwtIHJpbzo6aW1wb3J0KGZpbGUgPSAiLi9kYXRvcy9kcml2ZXJzLmNzdiIpDQoNCnJlc3VsdGFkb3NbLCBjKDYsOSldIDwtIHNhcHBseShyZXN1bHRhZG9zWywgYyg2LDkpXSwgYXMubnVtZXJpYykgI3RyYW5zZm9ybW8gdmFyaWFibGVzIGdyaWQgeSBwb3NpdGlvbk9yZGVyIGVuIG51bWVyaWNvDQpzdHIocmVzdWx0YWRvcykgIyBwYXJhIGNvbXByb2JhcmxvDQoNCg0KI21heW9yZXMgcmVtb250YWRhcyBkZSBsYSBoaXN0b3JpYSwgc2UgcmVzdGEgcG9zaWNpb24gZGUgc2FsaWRhIC0gcG9zaWNpb24gZmluYWwNCnB1ZXN0b3NfcmVtb250YWRvcyA8LSByZXN1bHRhZG9zICU+JSBtdXRhdGUocmVtb250YWRvcyA9IGdyaWQgLSBwb3NpdGlvbk9yZGVyKSAlPiUgc2VsZWN0KHJhY2VJZCwgZHJpdmVySWQsIGdyaWQsIHBvc2l0aW9uT3JkZXIsIHJlbW9udGFkb3MpIA0KDQoNCiNkZSB0b2RhIGxhIGhpc3RvcmlhDQpjaXJjdWl0b3NfZ3AgPC0gZnVsbF9qb2luKGNhcnJlcmFzLCBjaXJjdWl0b3MsIGMoImNpcmN1aXRJZCIgPSAiY2lyY3VpdElkIikpICU+JSBzZWxlY3QoY2lyY3VpdElkLCBuYW1lLnksIHJhY2VJZCwgeWVhcikNCg0KcHRvc19yZW1vbnRfY2FycmVyYSA8LSBpbm5lcl9qb2luKHB1ZXN0b3NfcmVtb250YWRvcywgY2lyY3VpdG9zX2dwKQ0KDQpwdWVzdG9zX3JlbW9udF9waWxvdG8gPC0gZnVsbF9qb2luKHBpbG90b3MsIHB0b3NfcmVtb250X2NhcnJlcmEsIGMoImRyaXZlcklkIiA9ICJkcml2ZXJJZCIpKSAlPiUgc2xpY2VfbWF4KHJlbW9udGFkb3MsIG49MTApICU+JSBzZWxlY3QoZHJpdmVySWQsIGRyaXZlclJlZixuYW1lLnkseWVhciwgcmFjZUlkLCBncmlkLCBwb3NpdGlvbk9yZGVyLCByZW1vbnRhZG9zKQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgZGUgbGEgaGlzb3RvcmlhIHJlY2llbnRlDQpjaXJjdWl0b3NfZ3BfcmVjaWVudCA8LSBmdWxsX2pvaW4oY2FycmVyYXMsIGNpcmN1aXRvcywgYygiY2lyY3VpdElkIiA9ICJjaXJjdWl0SWQiKSkgJT4lIHNlbGVjdChjaXJjdWl0SWQsIG5hbWUueSwgcmFjZUlkLCB5ZWFyKSAlPiUgZmlsdGVyKHllYXIgPj0gMTk5NSkNCg0KcHRvc19yZW1vbnRfY2FycmVyYV9yZWNpZW50IDwtIGlubmVyX2pvaW4ocHVlc3Rvc19yZW1vbnRhZG9zLCBjaXJjdWl0b3NfZ3BfcmVjaWVudCkNCg0KcHVlc3Rvc19yZW1vbnRfcGlsb3RvX3JlY2llbnQgPC0gZnVsbF9qb2luKHBpbG90b3MsIHB0b3NfcmVtb250X2NhcnJlcmFfcmVjaWVudCwgYygiZHJpdmVySWQiID0gImRyaXZlcklkIikpICU+JSBzbGljZV9tYXgocmVtb250YWRvcywgbj0xMCkgJT4lIHNlbGVjdChkcml2ZXJJZCwgZHJpdmVyUmVmLCBuYW1lLnksIHllYXIscmFjZUlkLCBncmlkLCBwb3NpdGlvbk9yZGVyLCByZW1vbnRhZG9zKSAlPiUgc2xpY2UoMTo0LDY6OCwxMCkgJT4lIGFycmFuZ2UoZGVzYyhyZW1vbnRhZG9zKSkNCg0KZ2dyZW1vbnRhZG9zIDwtIGdncGxvdChwdWVzdG9zX3JlbW9udF9waWxvdG9fcmVjaWVudCwgYWVzKHggPSByZW9yZGVyKGRyaXZlclJlZiwgcmVtb250YWRvcyksIHJlbW9udGFkb3MpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIGNvb3JkX2ZsaXAoKSArIGxhYnMoeCA9ICJQaWxvdG9zIiwgeSA9ICJOwrogZGUgcHVlc3RvcyByZW1vbnRhZG9zIiApDQpnZ3JlbW9udGFkb3MNCg0Kb2JqZXRvc19ub19ib3JyYXIgPC0gYygidmljdG9yaWFzX2Nvbl9ub21icmUiLCAibl9jYXJyZXJhc19ub20iLCAibWFzX3ZpY3RvcmlhcyIpDQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIG9iamV0b3Nfbm9fYm9ycmFyXSkNCmBgYA0KDQoNCiMgQUxPTlNPIHZzIEhBTUlMVE9ODQoNCmBgYHtyLCBldmFsPVRSVUUsIGVjaG89VFJVRX0NCg0KI3NlIG5lY2VzaXRhIHRlbmVyIGNhcmdhZG8gIm5fY2FycmVyYXNfbm9tIiwgInZpY3Rvcmlhc19jb25fbm9tYnJlIg0KDQpmb3Rvc19BTE9fdnNfSEFNIDwtIGMoIi4vaW1hZ2VuZXMvcGlsb3Rvcy9hbG9uc28ucG5nIiwgIi4vaW1hZ2VuZXMvcGlsb3Rvcy9oYW1pbHRvbi5wbmciKQ0KZm90b3NfZXNwX2luZyA8LSBjKCIuL2ltYWdlbmVzL3BhaXNlcy9lc3BhbnlhLnBuZyIsICIuL2ltYWdlbmVzL3BhaXNlcy91ay5wbmciKQ0Kbl9jYXJyZXJhc19hbG9faGFtIDwtIG5fY2FycmVyYXNfbm9tICU+JSBmaWx0ZXIoZHJpdmVyUmVmICVpbiUgYygiYWxvbnNvIiwgImhhbWlsdG9uIikpDQoNCm5fdmljdG9yaWFzX2Fsb19oYW0gPC0gdmljdG9yaWFzX2Nvbl9ub21icmUgJT4lIGZpbHRlcihkcml2ZXJSZWYgJWluJSBjKCJhbG9uc28iLCAiaGFtaWx0b24iKSkNCg0KYWxvX3ZzX2hhbSA8LSBmdWxsX2pvaW4obl9jYXJyZXJhc19hbG9faGFtLCBuX3ZpY3Rvcmlhc19hbG9faGFtLCBjKCJkcml2ZXJSZWYiPSAiZHJpdmVyUmVmIikpICU+JSBzZWxlY3QoIGRyaXZlclJlZiwgbnVtZXJvX2NhcnJlcmFzLCBuX3ZpY3RvcmlhcykgJT4lIGFkZF9jb2x1bW4oZm90b3NfZXNwX2luZywgZm90b3NfQUxPX3ZzX0hBTSkgDQoNCmxpYnJhcnkoZ3QpDQphbG9fdnNfaGFtX3RhYmxhIDwtIGFsb192c19oYW0gJT4lIGd0KCkgJT4lIHRleHRfdHJhbnNmb3JtKCBsb2NhdGlvbnMgPSBjZWxsc19ib2R5KGNvbHVtbnMgPSBjKGZvdG9zX2VzcF9pbmcpKSwgZm4gPSBmdW5jdGlvbih4KSB7Z3Q6OmxvY2FsX2ltYWdlKHgsIGhlaWdodCA9IDUwKX0pICU+JSB0ZXh0X3RyYW5zZm9ybSggbG9jYXRpb25zID0gY2VsbHNfYm9keShjb2x1bW5zID0gYyhmb3Rvc19BTE9fdnNfSEFNKSksIGZuID0gZnVuY3Rpb24oeCkge2d0Ojpsb2NhbF9pbWFnZSh4LCBoZWlnaHQgPSAxMDApfSkgJT4lIHRhYl9oZWFkZXIodGl0bGUgPSBtZCgiKipBbG9uc28gdnMgSGFtaWx0b24qKiIpLCBzdWJ0aXRsZSA9IG1kKCJDb21wYXJhY2nDs24iKSkgJT4lICAgY29sc19sYWJlbCgNCiAgICBkcml2ZXJSZWYgPSBodG1sKCIiKSwNCiAgICBudW1lcm9fY2FycmVyYXMgPSBodG1sKCJOwrogY2FycmVyYXMiKSwNCiAgICBuX3ZpY3RvcmlhcyA9IGh0bWwoIk7CuiB2aWN0b3JpYXMiKSwNCiAgICBmb3Rvc19lc3BfaW5nID0gaHRtbCgiUGHDrXMiKSwNCiAgICBmb3Rvc19BTE9fdnNfSEFNID0gaHRtbCgiIikpICU+JSAgDQogIHRhYl9vcHRpb25zKHRhYmxlLmJhY2tncm91bmQuY29sb3IgPSAiZ3JheTEzIiwgICB0YWJsZS5mb250LmNvbG9yLmxpZ2h0ID0gImN5YW4iKSAlPiUgDQogIGNvbHNfYWxpZ24oYWxpZ24gPSAiY2VudGVyIiwNCiAgY29sdW1ucyA9IGV2ZXJ5dGhpbmcoKSkNCg0KYWxvX3ZzX2hhbV90YWJsYQ0KYGBgDQoNCg0KYGBge3IsIGV2YWwgPSBUUlVFLCBlY2hvID0gVFJVRX0NCiNhdWRpZW5jaWFzDQoNCg0KYXVkaWVuY2lhcyA8LSByaW86OmltcG9ydChmaWxlID0gIi4vZGF0b3MvYXVkaWVuY2lhc0YxLmNzdiIpDQoNCmdnYXVkaWVuY2lhcyA8LSBnZ3Bsb3QoYXVkaWVuY2lhcywgYWVzKHggPSBBw7FvLCB5ID0gbnVtZXJvX2VzcGVjdGFkb3JlcykpICsgDQogIGdlb21faGlzdG9ncmFtIChzdGF0ID0gImlkZW50aXR5IikgKyANCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIk51bWVybyBkZSBlc3BlY3RhZG9yZXMiICkgKw0KICAgIHNjYWxlX3hfY29udGludW91cygNCiAgICBicmVha3MgPSBzZXEoMjAwNCwgMjAyMCwgMSksDQogICAgbGltaXRzID0gYygyMDAzLCAyMDIxKSkgIysgdHJhbnNpdGlvbl9yZXZlYWwoQcOxbykNCmdnYXVkaWVuY2lhcw0KDQpybShsaXN0ID0gbHMoKSkNCmBgYA0KDQoNCmBgYHtyLCBldmFsID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQuZXh0cmE9J2FuZ2xlPTkwJywgZWNobyA9IFRSVUV9DQojcHJlc3VwdWVzdG9zDQoNCnByZXN1cHVlc3RvcyA8LSByZWFkX2V4Y2VsKCJkYXRvcy9wcmVzdXB1ZXN0b3MueGxzeCIpDQoNCmdnX3ByZXN1cCA8LSBnZ3Bsb3QocHJlc3VwdWVzdG9zLCBhZXMoeWVhciwgUHJlc3VwdWVzdG8sIGNvbG9yID0gRXNjdWRlcmlhKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkgKyANCiAgbGFicyh4ID0gIkHDsW8iLCB5ID0gIlByZXN1cHVlc3RvIGVuIOKCrCIgKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGJyZWFrcyA9IHNlcSgyMDE1LCAyMDIzLCAxKSwNCiAgICBsaW1pdHMgPSBjKDIwMTQsIDIwMjQpKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoIGJyZWFrcyA9IHNlcSgwLCA3MDAwMDAwMDAsIDEwMDAwMDAwMCksDQogICAgbGltaXRzID0gYygwLCA2MDAwMDAwMDApKSANCg0KZ2dwbG90bHkoZ2dfcHJlc3VwKQ0KDQoNCnJtKGxpc3QgPSBscygpKQ0KYGBgDQojIyAqKlRIRSBQTEFOKioNCg0KYGBge3IsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0NCmxpYnJhcnkobWVtZSkNCiNteV9mb3RvIDwtICJodHRwczovL2UwMC1lbG11bmRvLnVlY2RuLmVzL2Fzc2V0cy9tdWx0aW1lZGlhL2ltYWdlbmVzLzIwMTcvMDUvMTIvMTQ5NDU4OTkzNDYzMDYuanBnIg0KI21lbWUobXlfZm90bywgIk1VWSBUUkFOUVVJTE9TIiwgIlNFIFZJRU5FIFRSQUJBSkFaTyIsIHNpemUgPSAyICwgY29sb3IgPSAiYmx1ZSIsIHZqdXN0ID0gMS4wNSkNCg0KYGBgDQoNCg0KDQojIDIuIERhdG9zDQoNCkhlbW9zIGVuY29udHJhZG8gZW4gW2thZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS8pIGJhc3RhbnRlcyBjb25qdW50b3MgZGUgZGF0b3MgY29uIGxvcyBxdWUgcG9kZXIgdHJhYmFqYXIsIHBlcm8gZXNwZWNpYWxtZW50ZSBbZXN0ZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9yb2hhbnJhby9mb3JtdWxhLTEtd29ybGQtY2hhbXBpb25zaGlwLTE5NTAtMjAyMCksIHF1ZSBwb3NlZSBncmFuIHZhcmllZGFkIGRlIGRhdG9zIGVuIGxvIHJlZmVyZW50ZSBhIHBpbG90b3MsIHJlc3VsdGFkb3MsIGNpcmN1aXRvcywgdGllbXBvcywgZXRjLi4uIENvbnNpZGVyYW1vcyBxdWUgcGFyYSBlbXBlemFyIGEgdHJhYmFqYXIgc2Vyw6Egc3VmaWNpZW50ZSwgeSBlbiBmdW5jacOzbiBkZSBjb21vIHZheWFtb3MgZGlyaWdpZW5kbyBlbCB0cmFiYWpvLCBidXNjYXJlbW9zIGRpZmVyZW50ZXMgY29uanVudG8gZGUgZGF0b3MgY29uIGxvcyBxdWUgYXBveWFybm9zLg0KDQojIDMuIFRyYWJham9zIGVuIGxvcyBxdWUgbm9zIHZhbW9zIGEgYmFzYXINCg0KQ29uIGxvcyBkYXRvcyBxdWUgaGVtb3MgZW5jb250cmFkbywgZXhpc3RlbiB1bmEgc2VyaWUgZGUgY8OzZGlnb3MgcXVlIHlhIHRyYWJhamFuIGNvbiBlc3RvcyBkYXRvcywgZXNwZWNpYWxtZW50ZSBbZXN0ZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9la3JlbWJheWFyL2Zvcm11bGEtMS03MHRoLWFubml2ZXJzYXJ5KSwgcXVlIGhhIGNvbnNlZ3VpZG8gcmVhbGl6YXIgYW7DoWxpc2lzIGNvbiBlc3RlIGNvbmp1bnRvIGRlIGRhdG9zIHkgdmFyaWFzIGlsdXN0cmFjaW9uZXMgbXV5IGxsYW1hdGl2YXMsIHBvciBsbyBxdWUgcG9kcmVtb3MgdG9tYXJsbyBjb21vIHJlZmVyZW5jaWEgZHVyYW50ZSBlbCBpbmljaW8gZGVsIHRyYWJham8NCg0KPiAiTmFkaWUgZXMgbcOhcyByw6FwaWRvIHF1ZSBlbCBuYW5vIg0K